From ab1d54a8acc5bc366c5755c2fe5a1d63a70d1d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Humbert?= Date: Mon, 20 Feb 2023 10:37:48 +0100 Subject: [PATCH] feat: add verification method parameter to issue-credentials-2.0/send endpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accomodate credential sending for did methods that are not did:sov or did:key by adding an optional `verification_method` parameter to the send endpoint options. Signed-off-by: Clément Humbert --- .../v2_0/formats/ld_proof/handler.py | 18 +++++++++++++----- .../formats/ld_proof/tests/test_handler.py | 2 +- .../protocols/issue_credential/v2_0/manager.py | 6 ++++-- .../v2_0/models/cred_ex_record.py | 3 +++ .../protocols/issue_credential/v2_0/routes.py | 10 ++++++++++ 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py index fc850070b4..c4fccc02d5 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/handler.py @@ -5,7 +5,7 @@ from ......vc.ld_proofs.check import get_properties_without_context import logging -from typing import Mapping +from typing import Mapping, Optional from marshmallow import EXCLUDE, INCLUDE @@ -253,7 +253,9 @@ async def _did_info_for_did(self, did: str) -> DIDInfo: # All other methods we can just query return await wallet.get_local_did(did) - async def _get_suite_for_detail(self, detail: LDProofVCDetail) -> LinkedDataProof: + async def _get_suite_for_detail( + self, detail: LDProofVCDetail, verification_method: Optional[str] = None + ) -> LinkedDataProof: issuer_id = detail.credential.issuer_id proof_type = detail.options.proof_type @@ -268,7 +270,9 @@ async def _get_suite_for_detail(self, detail: LDProofVCDetail) -> LinkedDataProo ) did_info = await self._did_info_for_did(issuer_id) - verification_method = self._get_verification_method(issuer_id) + verification_method = verification_method or self._get_verification_method( + issuer_id + ) suite = await self._get_suite( proof_type=proof_type, @@ -457,7 +461,9 @@ async def receive_request( """Receive linked data proof request.""" async def issue_credential( - self, cred_ex_record: V20CredExRecord, retries: int = 5 + self, + cred_ex_record: V20CredExRecord, + retries: int = 5, ) -> CredFormatAttachment: """Issue linked data proof credential.""" if not cred_ex_record.cred_request: @@ -472,7 +478,9 @@ async def issue_credential( detail = await self._prepare_detail(detail) # Get signature suite, proof purpose and document loader - suite = await self._get_suite_for_detail(detail) + suite = await self._get_suite_for_detail( + detail, cred_ex_record.verification_method + ) proof_purpose = self._get_proof_purpose( proof_purpose=detail.options.proof_purpose, challenge=detail.options.challenge, diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py index f0e11070e6..b89dad7da5 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/formats/ld_proof/tests/test_handler.py @@ -588,7 +588,7 @@ async def test_issue_credential(self): detail = LDProofVCDetail.deserialize(LD_PROOF_VC_DETAIL) - mock_get_suite.assert_called_once_with(detail) + mock_get_suite.assert_called_once_with(detail, None) mock_issue.assert_called_once_with( credential=LD_PROOF_VC_DETAIL["credential"], suite=mock_get_suite.return_value, diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py index ad6f3f9313..006217fabf 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/manager.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/manager.py @@ -55,6 +55,7 @@ async def prepare_send( self, connection_id: str, cred_proposal: V20CredProposal, + verification_method: Optional[str] = None, auto_remove: bool = None, ) -> Tuple[V20CredExRecord, V20CredOffer]: """ @@ -63,6 +64,7 @@ async def prepare_send( Args: connection_id: connection for which to create offer cred_proposal: credential proposal with preview + verification_method: an optional verification method to be used when issuing auto_remove: flag to remove the record automatically on completion Returns: @@ -73,6 +75,7 @@ async def prepare_send( auto_remove = not self._profile.settings.get("preserve_exchange_records") cred_ex_record = V20CredExRecord( connection_id=connection_id, + verification_method=verification_method, initiator=V20CredExRecord.INITIATOR_SELF, role=V20CredExRecord.ROLE_ISSUER, cred_proposal=cred_proposal, @@ -80,12 +83,11 @@ async def prepare_send( auto_remove=auto_remove, trace=(cred_proposal._trace is not None), ) - (cred_ex_record, cred_offer) = await self.create_offer( + return await self.create_offer( cred_ex_record=cred_ex_record, counter_proposal=None, comment="create automated v2.0 credential exchange record", ) - return (cred_ex_record, cred_offer) async def create_proposal( self, diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py index da88600943..448a82052a 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/models/cred_ex_record.py @@ -59,6 +59,7 @@ def __init__( *, cred_ex_id: str = None, connection_id: str = None, + verification_method: Optional[str] = None, thread_id: str = None, parent_thread_id: str = None, initiator: str = None, @@ -82,6 +83,7 @@ def __init__( super().__init__(cred_ex_id, state, trace=trace, **kwargs) self._id = cred_ex_id self.connection_id = connection_id or conn_id + self.verification_method = verification_method self.thread_id = thread_id self.parent_thread_id = parent_thread_id self.initiator = initiator @@ -217,6 +219,7 @@ def record_value(self) -> Mapping: prop: getattr(self, prop) for prop in ( "connection_id", + "verification_method", "parent_thread_id", "initiator", "role", diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index e3a066d339..129b98250f 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -277,6 +277,13 @@ class V20CredExFreeSchema(V20IssueCredSchemaCore): example=UUIDFour.EXAMPLE, # typically but not necessarily a UUID4 ) + verification_method = fields.Str( + required=False, + default=None, + allow_none=True, + description="For ld-proofs. Verification method for signing.", + ) + class V20CredBoundOfferRequestSchema(OpenAPISchema): """Request schema for sending bound credential offer admin message.""" @@ -628,6 +635,8 @@ async def credential_exchange_send(request: web.BaseRequest): comment = body.get("comment") connection_id = body.get("connection_id") + verification_method = body.get("verification_method") + filt_spec = body.get("filter") if not filt_spec: raise web.HTTPBadRequest(reason="Missing filter") @@ -668,6 +677,7 @@ async def credential_exchange_send(request: web.BaseRequest): cred_manager = V20CredManager(profile) (cred_ex_record, cred_offer_message) = await cred_manager.prepare_send( connection_id, + verification_method=verification_method, cred_proposal=cred_proposal, auto_remove=auto_remove, )