From a3c4e2951ea723b401d3e3a012b4ce9dbc246965 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 15 Nov 2021 12:23:10 -0800 Subject: [PATCH 1/7] problem report Signed-off-by: Shaanjot Gill --- .../present_proof/v2_0/formats/dif/handler.py | 55 +++++++++++++++---- .../v2_0/formats/dif/tests/test_handler.py | 32 +++++++++-- 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py index bf3de007ab..71335fb9e6 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py @@ -6,6 +6,7 @@ from typing import Mapping, Tuple, Sequence from uuid import uuid4 +from ......messaging.base_handler import BaseResponder from ......messaging.decorators.attach_decorator import AttachDecorator from ......storage.error import StorageNotFoundError from ......storage.vc_holder.base import VCHolder @@ -21,14 +22,17 @@ from ......wallet.base import BaseWallet from ......wallet.key_type import KeyType +from .....problem_report.v1_0.message import ProblemReport + from ....dif.pres_exch import PresentationDefinition, SchemaInputDescriptor -from ....dif.pres_exch_handler import DIFPresExchHandler +from ....dif.pres_exch_handler import DIFPresExchHandler, DIFPresExchError from ....dif.pres_proposal_schema import DIFProofProposalSchema from ....dif.pres_request_schema import ( DIFProofRequestSchema, DIFPresSpecSchema, ) from ....dif.pres_schema import DIFProofSchema +from ....v2_0.messages.pres_problem_report import ProblemReportReason from ...message_types import ( ATTACHMENT_FORMAT, @@ -339,15 +343,30 @@ async def create_pres( proof_type=dif_handler_proof_type, reveal_doc=reveal_doc_frame, ) - - pres = await dif_handler.create_vp( - challenge=challenge, - domain=domain, - pd=pres_definition, - credentials=credentials_list, - records_filter=limit_record_ids, - ) - return self.get_format_data(PRES_20, pres) + try: + pres = await dif_handler.create_vp( + challenge=challenge, + domain=domain, + pd=pres_definition, + credentials=credentials_list, + records_filter=limit_record_ids, + ) + return self.get_format_data(PRES_20, pres) + except DIFPresExchError as err: + LOGGER.error(str(err)) + responder = self._profile.inject_or(BaseResponder) + if responder: + report = ProblemReport( + description={ + "en": str(err), + "code": ProblemReportReason.ABANDONED.value, + } + ) + if pres_ex_record.thread_id: + report.assign_thread_id(pres_ex_record.thread_id) + await responder.send_reply( + report, connection_id=pres_ex_record.connection_id + ) async def process_vcrecords_return_list( self, vc_records: Sequence[VCRecord], record_ids: set @@ -385,7 +404,21 @@ async def receive_pres( pres_definition = PresentationDefinition.deserialize( proof_request.get("presentation_definition") ) - await dif_handler.verify_received_pres(pd=pres_definition, pres=dif_proof) + try: + await dif_handler.verify_received_pres(pd=pres_definition, pres=dif_proof) + except DIFPresExchError as err: + LOGGER.error(str(err)) + responder = self._profile.inject_or(BaseResponder) + if responder: + report = ProblemReport( + description={ + "en": str(err), + "code": ProblemReportReason.ABANDONED.value, + } + ) + await responder.send_reply( + report, connection_id=pres_ex_record.connection_id + ) async def verify_pres(self, pres_ex_record: V20PresExRecord) -> V20PresExRecord: """ diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index b1c217acd3..f8b5827084 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -16,6 +16,7 @@ BbsBlsSignatureProof2020, ) from .......vc.tests.document_loader import custom_document_loader +from .......vc.vc_ld.prove import sign_presentation, create_presentation, derive_credential from .......vc.vc_ld.validation_result import PresentationVerificationResult from .......wallet.base import BaseWallet @@ -37,6 +38,7 @@ from ...handler import V20PresFormatHandlerError from .. import handler as test_module +from .....dif.pres_exch_handler import DIFPresExchHandler as test_pe_handler_module from ..handler import DIFPresFormatHandler TEST_DID_SOV = "did:sov:LjgpST2rjsoxYegQDRm7EL" @@ -1652,8 +1654,11 @@ async def test_verify_received_pres_invalid_jsonpath(self): auto_present=True, error_msg="error", ) - with self.assertRaises(DIFPresExchError): + with async_mock.patch.object( + test_module.LOGGER, "error", async_mock.MagicMock() + ) as mock_log_err: await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + mock_log_err.assert_called_once() async def test_verify_received_pres_no_match_a(self): dif_proof_req = deepcopy(DIF_PRES_REQUEST_B) @@ -1702,8 +1707,11 @@ async def test_verify_received_pres_no_match_a(self): auto_present=True, error_msg="error", ) - with self.assertRaises(DIFPresExchError): + with async_mock.patch.object( + test_module.LOGGER, "error", async_mock.MagicMock() + ) as mock_log_err: await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + mock_log_err.assert_called_once() async def test_verify_received_pres_no_match_b(self): dif_proof_req = deepcopy(DIF_PRES_REQUEST_B) @@ -1752,8 +1760,11 @@ async def test_verify_received_pres_no_match_b(self): auto_present=True, error_msg="error", ) - with self.assertRaises(DIFPresExchError): + with async_mock.patch.object( + test_module.LOGGER, "error", async_mock.MagicMock() + ) as mock_log_err: await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + mock_log_err.assert_called_once() async def test_verify_received_pres_limit_disclosure_fail_a(self): dif_proof = deepcopy(DIF_PRES) @@ -1799,8 +1810,11 @@ async def test_verify_received_pres_limit_disclosure_fail_a(self): auto_present=True, error_msg="error", ) - with self.assertRaises(DIFPresExchError): + with async_mock.patch.object( + test_module.LOGGER, "error", async_mock.MagicMock() + ) as mock_log_err: await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + mock_log_err.assert_called_once() async def test_verify_received_pres_limit_disclosure_fail_b(self): dif_proof = deepcopy(DIF_PRES) @@ -1846,8 +1860,11 @@ async def test_verify_received_pres_limit_disclosure_fail_b(self): auto_present=True, error_msg="error", ) - with self.assertRaises(DIFPresExchError): + with async_mock.patch.object( + test_module.LOGGER, "error", async_mock.MagicMock() + ) as mock_log_err: await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + mock_log_err.assert_called_once() async def test_verify_received_pres_fail_schema_filter(self): dif_proof = deepcopy(DIF_PRES) @@ -1933,5 +1950,8 @@ async def test_verify_received_pres_fail_schema_filter(self): auto_present=True, error_msg="error", ) - with self.assertRaises(DIFPresExchError): + with async_mock.patch.object( + test_module.LOGGER, "error", async_mock.MagicMock() + ) as mock_log_err: await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + mock_log_err.assert_called_once() From a9c6185f275f6f6dc827619f8ecfe62602310d31 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 15 Nov 2021 12:51:36 -0800 Subject: [PATCH 2/7] updates Signed-off-by: Shaanjot Gill --- .../present_proof/dif/pres_exch_handler.py | 7 +- .../present_proof/dif/tests/test_data.py | 84 +++++ .../v2_0/formats/dif/tests/test_handler.py | 346 +++++++++--------- 3 files changed, 259 insertions(+), 178 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index d4207ad6dd..c3e5740c40 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -1366,9 +1366,6 @@ async def verify_received_pres( desc_map_item_id = desc_map_item.get("id") constraint = inp_desc_id_contraint_map.get(desc_map_item_id) schema_filter = inp_desc_id_schemas_map.get(desc_map_item_id) - is_one_of_filtered = False - if desc_map_item_id in inp_desc_id_schema_one_of_filter: - is_one_of_filtered = True desc_map_item_path = desc_map_item.get("path") jsonpath = parse(desc_map_item_path) match = jsonpath.find(pres) @@ -1378,7 +1375,7 @@ async def verify_received_pres( ) for match_item in match: if not await self.apply_constraint_received_cred( - constraint, match_item.value, is_one_of_filtered + constraint, match_item.value ): raise DIFPresExchError( f"Constraint specified for {desc_map_item_id} does not " @@ -1413,7 +1410,7 @@ async def restrict_field_paths_one_of_filter( return applied_field_paths async def apply_constraint_received_cred( - self, constraint: Constraints, cred_dict: dict, is_one_of_filtered: bool = False + self, constraint: Constraints, cred_dict: dict ) -> bool: """Evaluate constraint from the request against received credential.""" fields = constraint._fields diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_data.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_data.py index bd68947408..4ab8f602aa 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_data.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_data.py @@ -1625,6 +1625,25 @@ def create_vcrecord(cred_dict: dict, expanded_types: list): "@context": [ "https://www.w3.org/2018/credentials/v1", "https://w3id.org/security/bbs/v1", + { + "MedicalPass": { + "@id": "https://www.vdel.com/MedicalPass", + "@context": { + "description": "http://schema.org/description", + "identifier": "http://schema.org/identifier", + "name": "http://schema.org/name", + "image": "http://schema.org/image", + }, + } + }, + { + "Patient": { + "@id": "http://hl7.org/fhir/Patient", + "@context": [ + "https://fhircat.org/fhir-r5/rdf-r5/contexts/patient.context.jsonld" + ], + } + }, ], "id": "urn:bnid:_:c14n4", "type": ["MedicalPass", "VerifiableCredential"], @@ -1655,6 +1674,71 @@ def create_vcrecord(cred_dict: dict, expanded_types: list): "@context": [ "https://www.w3.org/2018/credentials/v1", "https://w3id.org/security/bbs/v1", + { + "LabReport": { + "@id": "https://www.vdel.com/LabReport", + "@context": { + "description": "http://schema.org/description", + "identifier": "http://schema.org/identifier", + "name": "http://schema.org/name", + "image": "http://schema.org/image", + }, + } + }, + { + "Specimen": { + "@id": "http://hl7.org/fhir/Specimen", + "@context": [ + None, + "https://fhircat.org/fhir-r5/rdf-r5/contexts/specimen.context.jsonld", + ], + } + }, + { + "Observation": { + "@id": "http://hl7.org/fhir/Observation", + "@context": [ + None, + "https://fhircat.org/fhir-r5/rdf-r5/contexts/observation.context.jsonld", + ], + } + }, + { + "Organization": { + "@id": "http://hl7.org/fhir/Organization", + "@context": [ + None, + "https://fhircat.org/fhir-r5/rdf-r5/contexts/organization.context.jsonld", + ], + } + }, + { + "Practitioner": { + "@id": "http://hl7.org/fhir/Practitioner", + "@context": [ + None, + "https://fhircat.org/fhir-r5/rdf-r5/contexts/practitioner.context.jsonld", + ], + } + }, + { + "DiagnosticReport": { + "@id": "http://hl7.org/fhir/DiagnosticReport", + "@context": [ + None, + "https://fhircat.org/fhir-r5/rdf-r5/contexts/diagnosticreport.context.jsonld", + ], + } + }, + { + "PractitionerRole": { + "@id": "http://hl7.org/fhir/PractitionerRole", + "@context": [ + None, + "https://fhircat.org/fhir-r5/rdf-r5/contexts/practitionerrole.context.jsonld", + ], + } + }, ], "type": ["VerifiableCredential", "LabReport"], "issuer": "did:key:zUC74FYQCzCbDpbVm9v1LVCc2RkxJY3XMdxV9UpsVaerTgEAAjpdWfE8WemccfdNhski3kHiXfLzPZW2wgsvSCkZFWV3zSNxQEqZoV8kVpwLtLzzpskRcskBB3M3DxaeBnDvK4H", diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index 8f5ff5174e..f8b5827084 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -1434,179 +1434,179 @@ async def test_verify_received_pres_c(self): ) await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) - # async def test_verify_received_limit_disclosure_a(self): - # dif_proof = deepcopy(DIF_PRES) - # cred_dict = deepcopy(TEST_CRED_DICT) - # cred_dict["credentialSubject"]["Patient"] = [ - # { - # "address": [ - # { - # "@id": "urn:bnid:_:c14n1", - # "city": "Рума", - # }, - # { - # "@id": "urn:bnid:_:c14n1", - # "city": "Рума", - # }, - # ] - # }, - # { - # "address": [ - # { - # "@id": "urn:bnid:_:c14n1", - # "city": "Рума", - # }, - # { - # "@id": "urn:bnid:_:c14n1", - # "city": "Рума", - # }, - # ] - # }, - # ] - # dif_proof["verifiableCredential"] = [] - # dif_proof["verifiableCredential"].append(cred_dict) - # dif_proof["verifiableCredential"].append(cred_dict) - # dif_pres = V20Pres( - # formats=[ - # V20PresFormat( - # attach_id="dif", - # format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], - # ) - # ], - # presentations_attach=[ - # AttachDecorator.data_json( - # mapping=dif_proof, - # ident="dif", - # ) - # ], - # ) - # pres_request = deepcopy(DIF_PRES_REQUEST_B) - # pres_request["presentation_definition"]["input_descriptors"][0][ - # "constraints" - # ] = { - # "limit_disclosure": "required", - # "fields": [ - # { - # "path": ["$.credentialSubject.Patient[0].address[*].city"], - # "purpose": "Test", - # } - # ], - # } - # pres_request["presentation_definition"]["input_descriptors"][0]["schema"] = { - # "oneof_filter": [ - # [ - # {"uri": "https://www.w3.org/2018/credentials#VerifiableCredential"}, - # {"uri": "https://www.vdel.com/MedicalPass"}, - # {"uri": "http://hl7.org/fhir/Patient"}, - # ], - # [ - # {"uri": "https://www.w3.org/2018/credentials#VerifiableCredential"}, - # {"uri": "https://w3id.org/citizenship#PermanentResidentCard"}, - # ], - # ] - # } - # dif_pres_request = V20PresRequest( - # formats=[ - # V20PresFormat( - # attach_id="dif", - # format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ - # V20PresFormat.Format.DIF.api - # ], - # ) - # ], - # request_presentations_attach=[ - # AttachDecorator.data_json(pres_request, ident="dif") - # ], - # ) - # record = V20PresExRecord( - # pres_ex_id="pxid", - # thread_id="thid", - # connection_id="conn_id", - # initiator="init", - # role="role", - # state="state", - # pres_request=dif_pres_request, - # pres=dif_pres, - # verified="false", - # auto_present=True, - # error_msg="error", - # ) - # await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) - - # async def test_verify_received_limit_disclosure_b(self): - # dif_proof = deepcopy(DIF_PRES) - # cred_dict = deepcopy(TEST_CRED_DICT) - # cred_dict["credentialSubject"]["Patient"]["address"] = [ - # { - # "@id": "urn:bnid:_:c14n1", - # "city": "Рума", - # }, - # { - # "@id": "urn:bnid:_:c14n1", - # "city": "Рума", - # }, - # ] - # dif_proof["verifiableCredential"] = [] - # dif_proof["verifiableCredential"].append(cred_dict) - # dif_proof["verifiableCredential"].append(cred_dict) - # dif_pres = V20Pres( - # formats=[ - # V20PresFormat( - # attach_id="dif", - # format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], - # ) - # ], - # presentations_attach=[ - # AttachDecorator.data_json( - # mapping=dif_proof, - # ident="dif", - # ) - # ], - # ) - # pres_request = deepcopy(DIF_PRES_REQUEST_B) - # pres_request["presentation_definition"]["input_descriptors"][0][ - # "constraints" - # ] = { - # "limit_disclosure": "required", - # "fields": [ - # { - # "path": ["$.credentialSubject.Patient[*].address"], - # "purpose": "Test", - # } - # ], - # } - # pres_request["presentation_definition"]["input_descriptors"][0]["schema"] = [ - # {"uri": "https://www.w3.org/2018/credentials#VerifiableCredential"}, - # {"uri": "https://www.vdel.com/MedicalPass"}, - # {"uri": "http://hl7.org/fhir/Patient"}, - # ] - # dif_pres_request = V20PresRequest( - # formats=[ - # V20PresFormat( - # attach_id="dif", - # format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ - # V20PresFormat.Format.DIF.api - # ], - # ) - # ], - # request_presentations_attach=[ - # AttachDecorator.data_json(pres_request, ident="dif") - # ], - # ) - # record = V20PresExRecord( - # pres_ex_id="pxid", - # thread_id="thid", - # connection_id="conn_id", - # initiator="init", - # role="role", - # state="state", - # pres_request=dif_pres_request, - # pres=dif_pres, - # verified="false", - # auto_present=True, - # error_msg="error", - # ) - # await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + async def test_verify_received_limit_disclosure_a(self): + dif_proof = deepcopy(DIF_PRES) + cred_dict = deepcopy(TEST_CRED_DICT) + cred_dict["credentialSubject"]["Patient"] = [ + { + "address": [ + { + "@id": "urn:bnid:_:c14n1", + "city": "Рума", + }, + { + "@id": "urn:bnid:_:c14n1", + "city": "Рума", + }, + ] + }, + { + "address": [ + { + "@id": "urn:bnid:_:c14n1", + "city": "Рума", + }, + { + "@id": "urn:bnid:_:c14n1", + "city": "Рума", + }, + ] + }, + ] + dif_proof["verifiableCredential"] = [] + dif_proof["verifiableCredential"].append(cred_dict) + dif_proof["verifiableCredential"].append(cred_dict) + dif_pres = V20Pres( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], + ) + ], + presentations_attach=[ + AttachDecorator.data_json( + mapping=dif_proof, + ident="dif", + ) + ], + ) + pres_request = deepcopy(DIF_PRES_REQUEST_B) + pres_request["presentation_definition"]["input_descriptors"][0][ + "constraints" + ] = { + "limit_disclosure": "required", + "fields": [ + { + "path": ["$.credentialSubject.Patient[0].address[*].city"], + "purpose": "Test", + } + ], + } + pres_request["presentation_definition"]["input_descriptors"][0]["schema"] = { + "oneof_filter": [ + [ + {"uri": "https://www.w3.org/2018/credentials#VerifiableCredential"}, + {"uri": "https://www.vdel.com/MedicalPass"}, + {"uri": "http://hl7.org/fhir/Patient"}, + ], + [ + {"uri": "https://www.w3.org/2018/credentials#VerifiableCredential"}, + {"uri": "https://w3id.org/citizenship#PermanentResidentCard"}, + ], + ] + } + dif_pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_json(pres_request, ident="dif") + ], + ) + record = V20PresExRecord( + pres_ex_id="pxid", + thread_id="thid", + connection_id="conn_id", + initiator="init", + role="role", + state="state", + pres_request=dif_pres_request, + pres=dif_pres, + verified="false", + auto_present=True, + error_msg="error", + ) + await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + + async def test_verify_received_limit_disclosure_b(self): + dif_proof = deepcopy(DIF_PRES) + cred_dict = deepcopy(TEST_CRED_DICT) + cred_dict["credentialSubject"]["Patient"]["address"] = [ + { + "@id": "urn:bnid:_:c14n1", + "city": "Рума", + }, + { + "@id": "urn:bnid:_:c14n1", + "city": "Рума", + }, + ] + dif_proof["verifiableCredential"] = [] + dif_proof["verifiableCredential"].append(cred_dict) + dif_proof["verifiableCredential"].append(cred_dict) + dif_pres = V20Pres( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], + ) + ], + presentations_attach=[ + AttachDecorator.data_json( + mapping=dif_proof, + ident="dif", + ) + ], + ) + pres_request = deepcopy(DIF_PRES_REQUEST_B) + pres_request["presentation_definition"]["input_descriptors"][0][ + "constraints" + ] = { + "limit_disclosure": "required", + "fields": [ + { + "path": ["$.credentialSubject.Patient[*].address"], + "purpose": "Test", + } + ], + } + pres_request["presentation_definition"]["input_descriptors"][0]["schema"] = [ + {"uri": "https://www.w3.org/2018/credentials#VerifiableCredential"}, + {"uri": "https://www.vdel.com/MedicalPass"}, + {"uri": "http://hl7.org/fhir/Patient"}, + ] + dif_pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_json(pres_request, ident="dif") + ], + ) + record = V20PresExRecord( + pres_ex_id="pxid", + thread_id="thid", + connection_id="conn_id", + initiator="init", + role="role", + state="state", + pres_request=dif_pres_request, + pres=dif_pres, + verified="false", + auto_present=True, + error_msg="error", + ) + await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) async def test_verify_received_pres_invalid_jsonpath(self): dif_proof = deepcopy(DIF_PRES) From 4b5ed4155c669fafdcdc01408b571ea7c7a07f37 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 15 Nov 2021 13:07:14 -0800 Subject: [PATCH 3/7] format fix Signed-off-by: Shaanjot Gill --- .../present_proof/v2_0/formats/dif/tests/test_handler.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index f8b5827084..2d65abecb4 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -16,7 +16,11 @@ BbsBlsSignatureProof2020, ) from .......vc.tests.document_loader import custom_document_loader -from .......vc.vc_ld.prove import sign_presentation, create_presentation, derive_credential +from .......vc.vc_ld.prove import ( + sign_presentation, + create_presentation, + derive_credential, +) from .......vc.vc_ld.validation_result import PresentationVerificationResult from .......wallet.base import BaseWallet From 26054d409ce968884ec503e833fbca4f636818fd Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Mon, 15 Nov 2021 17:11:08 -0800 Subject: [PATCH 4/7] test updates Signed-off-by: Shaanjot Gill --- .../present_proof/dif/tests/test_data.py | 125 ++++++++++ .../dif/tests/test_pres_exch_handler.py | 224 ++++++++++++------ .../v2_0/formats/dif/tests/test_handler.py | 24 +- 3 files changed, 295 insertions(+), 78 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_data.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_data.py index 4ab8f602aa..125ca93419 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_data.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_data.py @@ -49,6 +49,131 @@ def create_vcrecord(cred_dict: dict, expanded_types: list): ) +EXPANDED_CRED_FHIR_TYPE_1 = [ + { + "https://www.w3.org/2018/credentials#credentialSubject": [ + { + "http://hl7.org/fhir/Patient": [ + { + "@id": "urn:bnid:_:c14n7", + "@type": ["http://hl7.org/fhir/resource-types#Patient"], + "http://hl7.org/fhir/Patient.address": [ + { + "@id": "urn:bnid:_:c14n1", + "http://hl7.org/fhir/Address.city": [ + {"@value": "Рума"} + ], + "http://hl7.org/fhir/Address.country": [ + {"@value": "test"} + ], + }, + { + "@id": "urn:bnid:_:c14n1", + "http://hl7.org/fhir/Address.city": [ + {"@value": "Рума"} + ], + }, + ], + } + ], + "@id": "urn:bnid:_:c14n6", + } + ], + "@id": "urn:bnid:_:c14n4", + "https://www.w3.org/2018/credentials#issuanceDate": [ + { + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + "@value": "2021-10-01T20:16:40+02:00", + } + ], + "https://www.w3.org/2018/credentials#issuer": [{"@id": "did:key:test"}], + "https://w3id.org/security#proof": [ + { + "@graph": [ + { + "http://purl.org/dc/terms/created": [ + { + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + "@value": "2021-10-01T18:16:41.072975+00:00", + } + ], + "https://w3id.org/security#nonce": [ + { + "@value": "M9yQx0eKIAI3Zs0sLF1kQsO7/hV1ZKEnqX9f0V/SzwRMKEixa0tJgqbGwviMA04XoL0=" + } + ], + "https://w3id.org/security#proofPurpose": [ + {"@id": "https://w3id.org/security#assertionMethod"} + ], + "https://w3id.org/security#proofValue": [ + { + "@value": "ACYgFHwPj5TxR9H+k+V+rBsfZ3SgOEvoKrYCcvAl4HhaKNR039r5UWE89tnHaVOx22k604EWibf0s7BTezijjYv1VWSVkZar4wtOslplXv6g7dVc8/0IWXQWOfn2hTE2N65Wv8xz2qw5dWwEzSXTx44o15wE2ubimgGFMM7Mv++SAoHC1dQGotGqKqOB2PS8yI+ToiWmswAAAHSD5NRIZHKeiWP8hK/e9xUYy5gSPBivDVAORybl62B/F3eaUC/pRdfsORAWRHLjmfcAAAACcOe6yrLqI3OmxkKUfsCGgIl83LLcQ9pLjaigdc/5XRs6KYo533Q/7cGryn2IvLFAJiHgZJ8Ovwi9xkDy1USKjZfjgRMil4PEiwZ2Gqu4g+HlJ11JemUX2HDAjJYgJHSFguZp/l/5y//0pQegHOi9hwAAABcp9nblpM/ALrFpdenGn23x5kdYC4gMyTV6a6RPuMwryVZcmTP50XDVHiY2t4JLvULdJcGDcOCpetMPhqyAf3VeNtorYjr1+YWSgjApfqZ594rMyohWGwkNu0zqv19qDkQ+aBibGhhsCBHe+jFy/BXQv2TlIMgX7YdUgVtUuO4YJT4cz4xrDlK58sJPpmJqraasoA0E+ciPOtGX5J7e4n+dGlPwkQjcD79cjBGs7hXmljeqbe2a82YQw/Q+L/yVKqxl8+ucLoqQ2QzREKslQ7ljchX8RsHQURflZTgPxGjNyCqHtEIcT6d7COcpmqGYSo5ge0pIXab97H97NBnk9mmdcCOCETWOJ8shuS7n4R4GdnRDjB5ArbBnpIMYUGEsdD0ZR87nVBbAfWFhQWJgsJvpPOGq2p6VPImfwhIoh7LIYkpwVogRLrSQGl5IZcHexlHwjZoogafCD5OSyEAO3au3UUoVde4S98v2233QuOwXvz3ptYOO+aJIbqmgdmGs41YfbyT830/H+248+Zbkob7T1FBWbYtEW+k8omat87tc3RfU9LYgNrXWUpJ/TZ+4Cqg7VljkPhCIEZYNUoKQxG1pP11HsmLvzhtnoNVLwjvJA7IrcinAr2pnWSBzjm/wBx8mANrCAHW4f4yyvSXCWZJOfnf/N8dt01Di0QaNbYs8Hlo6yjjjqkrvgLpZtAuuca8nQPPNZWrj3Oids/Z0nZsgKGwZxHo5negKE1JKEEz7zJQUd14JhRYiwfzWYprHcJ9szp5Tgmskksv3NIyKQ7XfLwnOY29zLOpTm51c99Ru6CVvAvIGckB+oE8cwPRjfE9fajJtQEODZ1ljbzYNACzLZ52iSsL+rSKq9LL79TgmN2lE0SkmgrwkOBAjmSwzrBc9DdQrkpWlSZzOWyL/QuNfHfEiNn43nwhaJpbvQ6zr/XHbspH7oqe0eexfvzowzkKc9noWqQnU0IaMrtRgyOma" + } + ], + "@type": ["https://w3id.org/security#BbsBlsSignatureProof2020"], + "https://w3id.org/security#verificationMethod": [ + {"@id": "did:key:test"} + ], + } + ] + } + ], + "@type": [ + "https://www.vdel.com/MedicalPass", + "https://www.w3.org/2018/credentials#VerifiableCredential", + ], + } +] + +EXPANDED_CRED_FHIR_TYPE_2 = [ + { + "https://www.w3.org/2018/credentials#credentialSubject": [{}], + "@id": "urn:bnid:_:c14n4", + "https://www.w3.org/2018/credentials#issuanceDate": [ + { + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + "@value": "2021-10-01T20:16:40+02:00", + } + ], + "https://www.w3.org/2018/credentials#issuer": [{"@id": "did:key:test"}], + "https://w3id.org/security#proof": [ + { + "@graph": [ + { + "http://purl.org/dc/terms/created": [ + { + "@type": "http://www.w3.org/2001/XMLSchema#dateTime", + "@value": "2021-10-01T18:16:41.072975+00:00", + } + ], + "https://w3id.org/security#nonce": [ + { + "@value": "M9yQx0eKIAI3Zs0sLF1kQsO7/hV1ZKEnqX9f0V/SzwRMKEixa0tJgqbGwviMA04XoL0=" + } + ], + "https://w3id.org/security#proofPurpose": [ + {"@id": "https://w3id.org/security#assertionMethod"} + ], + "https://w3id.org/security#proofValue": [ + { + "@value": "ACYgFHwPj5TxR9H+k+V+rBsfZ3SgOEvoKrYCcvAl4HhaKNR039r5UWE89tnHaVOx22k604EWibf0s7BTezijjYv1VWSVkZar4wtOslplXv6g7dVc8/0IWXQWOfn2hTE2N65Wv8xz2qw5dWwEzSXTx44o15wE2ubimgGFMM7Mv++SAoHC1dQGotGqKqOB2PS8yI+ToiWmswAAAHSD5NRIZHKeiWP8hK/e9xUYy5gSPBivDVAORybl62B/F3eaUC/pRdfsORAWRHLjmfcAAAACcOe6yrLqI3OmxkKUfsCGgIl83LLcQ9pLjaigdc/5XRs6KYo533Q/7cGryn2IvLFAJiHgZJ8Ovwi9xkDy1USKjZfjgRMil4PEiwZ2Gqu4g+HlJ11JemUX2HDAjJYgJHSFguZp/l/5y//0pQegHOi9hwAAABcp9nblpM/ALrFpdenGn23x5kdYC4gMyTV6a6RPuMwryVZcmTP50XDVHiY2t4JLvULdJcGDcOCpetMPhqyAf3VeNtorYjr1+YWSgjApfqZ594rMyohWGwkNu0zqv19qDkQ+aBibGhhsCBHe+jFy/BXQv2TlIMgX7YdUgVtUuO4YJT4cz4xrDlK58sJPpmJqraasoA0E+ciPOtGX5J7e4n+dGlPwkQjcD79cjBGs7hXmljeqbe2a82YQw/Q+L/yVKqxl8+ucLoqQ2QzREKslQ7ljchX8RsHQURflZTgPxGjNyCqHtEIcT6d7COcpmqGYSo5ge0pIXab97H97NBnk9mmdcCOCETWOJ8shuS7n4R4GdnRDjB5ArbBnpIMYUGEsdD0ZR87nVBbAfWFhQWJgsJvpPOGq2p6VPImfwhIoh7LIYkpwVogRLrSQGl5IZcHexlHwjZoogafCD5OSyEAO3au3UUoVde4S98v2233QuOwXvz3ptYOO+aJIbqmgdmGs41YfbyT830/H+248+Zbkob7T1FBWbYtEW+k8omat87tc3RfU9LYgNrXWUpJ/TZ+4Cqg7VljkPhCIEZYNUoKQxG1pP11HsmLvzhtnoNVLwjvJA7IrcinAr2pnWSBzjm/wBx8mANrCAHW4f4yyvSXCWZJOfnf/N8dt01Di0QaNbYs8Hlo6yjjjqkrvgLpZtAuuca8nQPPNZWrj3Oids/Z0nZsgKGwZxHo5negKE1JKEEz7zJQUd14JhRYiwfzWYprHcJ9szp5Tgmskksv3NIyKQ7XfLwnOY29zLOpTm51c99Ru6CVvAvIGckB+oE8cwPRjfE9fajJtQEODZ1ljbzYNACzLZ52iSsL+rSKq9LL79TgmN2lE0SkmgrwkOBAjmSwzrBc9DdQrkpWlSZzOWyL/QuNfHfEiNn43nwhaJpbvQ6zr/XHbspH7oqe0eexfvzowzkKc9noWqQnU0IaMrtRgyOma" + } + ], + "@type": ["https://w3id.org/security#BbsBlsSignatureProof2020"], + "https://w3id.org/security#verificationMethod": [ + {"@id": "did:key:test"} + ], + } + ] + } + ], + "@type": [ + "https://www.vdel.com/MedicalPass", + "https://www.w3.org/2018/credentials#VerifiableCredential", + ], + } +] + is_holder_pd = PresentationDefinition.deserialize( { "id": "32f54163-7166-48f1-93d8-ff217bdb0653", diff --git a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py index 9129f77b0e..67a56badb0 100644 --- a/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/tests/test_pres_exch_handler.py @@ -53,6 +53,8 @@ is_holder_pd, is_holder_pd_multiple_fields_excluded, is_holder_pd_multiple_fields_included, + EXPANDED_CRED_FHIR_TYPE_1, + EXPANDED_CRED_FHIR_TYPE_2, TEST_CRED_DICT, TEST_CRED_WILDCARD, ) @@ -3262,9 +3264,13 @@ async def test_apply_constraint_received_cred_path_update(self, profile): ], } constraint = Constraints.deserialize(constraint) - assert await dif_pres_exch_handler.apply_constraint_received_cred( - constraint=constraint, cred_dict=cred_dict - ) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1 + assert await dif_pres_exch_handler.apply_constraint_received_cred( + constraint=constraint, cred_dict=cred_dict + ) @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures @@ -3294,9 +3300,13 @@ async def test_apply_constraint_received_cred_invalid(self, profile): ], } constraint = Constraints.deserialize(constraint) - assert not await dif_pres_exch_handler.apply_constraint_received_cred( - constraint=constraint, cred_dict=cred_dict - ) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1 + assert not await dif_pres_exch_handler.apply_constraint_received_cred( + constraint=constraint, cred_dict=cred_dict + ) constraint = { "limit_disclosure": "required", @@ -3308,9 +3318,13 @@ async def test_apply_constraint_received_cred_invalid(self, profile): ], } constraint = Constraints.deserialize(constraint) - assert not await dif_pres_exch_handler.apply_constraint_received_cred( - constraint=constraint, cred_dict=cred_dict - ) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1 + assert not await dif_pres_exch_handler.apply_constraint_received_cred( + constraint=constraint, cred_dict=cred_dict + ) @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures @@ -3330,9 +3344,13 @@ async def test_apply_constraint_received_cred_valid(self, profile): ], } constraint = Constraints.deserialize(constraint) - assert await dif_pres_exch_handler.apply_constraint_received_cred( - constraint=constraint, cred_dict=cred_dict - ) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1 + assert await dif_pres_exch_handler.apply_constraint_received_cred( + constraint=constraint, cred_dict=cred_dict + ) constraint = { "limit_disclosure": "required", @@ -3344,9 +3362,13 @@ async def test_apply_constraint_received_cred_valid(self, profile): ], } constraint = Constraints.deserialize(constraint) - assert await dif_pres_exch_handler.apply_constraint_received_cred( - constraint=constraint, cred_dict=cred_dict - ) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1 + assert await dif_pres_exch_handler.apply_constraint_received_cred( + constraint=constraint, cred_dict=cred_dict + ) cred_dict["credentialSubject"]["Patient"]["address"] = [ { @@ -3368,9 +3390,13 @@ async def test_apply_constraint_received_cred_valid(self, profile): ], } constraint = Constraints.deserialize(constraint) - assert await dif_pres_exch_handler.apply_constraint_received_cred( - constraint=constraint, cred_dict=cred_dict - ) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1 + assert await dif_pres_exch_handler.apply_constraint_received_cred( + constraint=constraint, cred_dict=cred_dict + ) cred_dict["credentialSubject"]["Patient"] = [ { @@ -3408,9 +3434,13 @@ async def test_apply_constraint_received_cred_valid(self, profile): ], } constraint = Constraints.deserialize(constraint) - assert await dif_pres_exch_handler.apply_constraint_received_cred( - constraint=constraint, cred_dict=cred_dict - ) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 + assert await dif_pres_exch_handler.apply_constraint_received_cred( + constraint=constraint, cred_dict=cred_dict + ) constraint = { "limit_disclosure": "required", @@ -3422,9 +3452,13 @@ async def test_apply_constraint_received_cred_valid(self, profile): ], } constraint = Constraints.deserialize(constraint) - assert await dif_pres_exch_handler.apply_constraint_received_cred( - constraint=constraint, cred_dict=cred_dict - ) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 + assert await dif_pres_exch_handler.apply_constraint_received_cred( + constraint=constraint, cred_dict=cred_dict + ) constraint = { "limit_disclosure": "required", @@ -3436,9 +3470,13 @@ async def test_apply_constraint_received_cred_valid(self, profile): ], } constraint = Constraints.deserialize(constraint) - assert await dif_pres_exch_handler.apply_constraint_received_cred( - constraint=constraint, cred_dict=cred_dict - ) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 + assert await dif_pres_exch_handler.apply_constraint_received_cred( + constraint=constraint, cred_dict=cred_dict + ) constraint = { "limit_disclosure": "required", @@ -3450,9 +3488,13 @@ async def test_apply_constraint_received_cred_valid(self, profile): ], } constraint = Constraints.deserialize(constraint) - assert await dif_pres_exch_handler.apply_constraint_received_cred( - constraint=constraint, cred_dict=cred_dict - ) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 + assert await dif_pres_exch_handler.apply_constraint_received_cred( + constraint=constraint, cred_dict=cred_dict + ) @pytest.mark.asyncio @pytest.mark.ursa_bbs_signatures @@ -3470,9 +3512,13 @@ async def test_apply_constraint_received_cred_no_sel_disc(self, profile): ], } constraint = Constraints.deserialize(constraint) - assert not await dif_pres_exch_handler.apply_constraint_received_cred( - constraint=constraint, cred_dict=cred_dict - ) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1 + assert not await dif_pres_exch_handler.apply_constraint_received_cred( + constraint=constraint, cred_dict=cred_dict + ) @pytest.mark.asyncio async def test_get_updated_path(self, profile): @@ -3553,13 +3599,19 @@ async def test_filter_by_field_keyerror(self, profile): "@type": "fhir:resource-types#Patient", "address": {"@id": "urn:bnid:_:c14n1", "city": "Рума"}, } - vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) - field = DIFField.deserialize( - { - "path": ["$.credentialSubject.Patient[0].address[0].city"], - } - ) - assert not await dif_pres_exch_handler.filter_by_field(field, vc_record_cred) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1 + vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) + field = DIFField.deserialize( + { + "path": ["$.credentialSubject.Patient[0].address[0].city"], + } + ) + assert not await dif_pres_exch_handler.filter_by_field( + field, vc_record_cred + ) @pytest.mark.asyncio async def test_filter_by_field_xsd_parser(self, profile): @@ -3572,55 +3624,75 @@ async def test_filter_by_field_xsd_parser(self, profile): "type": "xsd:integer", "@value": "10", } - vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) - field = DIFField.deserialize( - { - "path": ["$.credentialSubject.lprNumber"], - "filter": { - "minimum": 5, - "type": "number", - }, - } - ) - assert await dif_pres_exch_handler.filter_by_field(field, vc_record_cred) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 + vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) + field = DIFField.deserialize( + { + "path": ["$.credentialSubject.lprNumber"], + "filter": { + "minimum": 5, + "type": "number", + }, + } + ) + assert await dif_pres_exch_handler.filter_by_field(field, vc_record_cred) cred_dict = deepcopy(TEST_CRED_DICT) cred_dict["credentialSubject"] = {} cred_dict["credentialSubject"]["testDate"] = { "type": "xsd:dateTime", "@value": "2020-09-28T11:00:00+00:00", } - vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) - field = DIFField.deserialize( - { - "path": ["$.credentialSubject.testDate"], - "filter": {"type": "string", "format": "date", "minimum": "2005-5-16"}, - } - ) - assert await dif_pres_exch_handler.filter_by_field(field, vc_record_cred) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 + vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) + field = DIFField.deserialize( + { + "path": ["$.credentialSubject.testDate"], + "filter": { + "type": "string", + "format": "date", + "minimum": "2005-5-16", + }, + } + ) + assert await dif_pres_exch_handler.filter_by_field(field, vc_record_cred) cred_dict = deepcopy(TEST_CRED_DICT) cred_dict["credentialSubject"] = {} cred_dict["credentialSubject"]["testFlag"] = { "type": "xsd:boolean", "@value": "false", } - vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) - field = DIFField.deserialize( - { - "path": ["$.credentialSubject.testFlag"], - } - ) - assert await dif_pres_exch_handler.filter_by_field(field, vc_record_cred) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 + vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) + field = DIFField.deserialize( + { + "path": ["$.credentialSubject.testFlag"], + } + ) + assert await dif_pres_exch_handler.filter_by_field(field, vc_record_cred) cred_dict = deepcopy(TEST_CRED_DICT) cred_dict["credentialSubject"] = {} cred_dict["credentialSubject"]["testDouble"] = { "type": "xsd:double", "@value": "10.2", } - vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) - field = DIFField.deserialize( - {"path": ["$.credentialSubject.testDouble"], "filter": {"const": 10.2}} - ) - assert await dif_pres_exch_handler.filter_by_field(field, vc_record_cred) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 + vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) + field = DIFField.deserialize( + {"path": ["$.credentialSubject.testDouble"], "filter": {"const": 10.2}} + ) + assert await dif_pres_exch_handler.filter_by_field(field, vc_record_cred) cred_dict = deepcopy(TEST_CRED_DICT) cred_dict["credentialSubject"] = {} cred_dict["credentialSubject"]["test"] = { @@ -3628,9 +3700,13 @@ async def test_filter_by_field_xsd_parser(self, profile): "@id": "test", "test": "val", } - vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) - field = DIFField.deserialize({"path": ["$.credentialSubject.test"]}) - assert await dif_pres_exch_handler.filter_by_field(field, vc_record_cred) + with async_mock.patch.object( + test_module.jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 + vc_record_cred = dif_pres_exch_handler.create_vcrecord(cred_dict) + field = DIFField.deserialize({"path": ["$.credentialSubject.test"]}) + assert await dif_pres_exch_handler.filter_by_field(field, vc_record_cred) def test_string_to_timezone_aware_datetime(self, profile): dif_pres_exch_handler = DIFPresExchHandler( diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index 2d65abecb4..09788ba581 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -2,6 +2,7 @@ from asynctest import TestCase as AsyncTestCase from asynctest import mock as async_mock from marshmallow import ValidationError +from pyld import jsonld from aries_cloudagent.protocols.present_proof.dif.pres_exch import SchemaInputDescriptor @@ -25,7 +26,11 @@ from .......wallet.base import BaseWallet from .....dif.pres_exch_handler import DIFPresExchHandler, DIFPresExchError -from .....dif.tests.test_data import TEST_CRED_DICT +from .....dif.tests.test_data import ( + TEST_CRED_DICT, + EXPANDED_CRED_FHIR_TYPE_1, + EXPANDED_CRED_FHIR_TYPE_2, +) from ....message_types import ( ATTACHMENT_FORMAT, @@ -1535,7 +1540,11 @@ async def test_verify_received_limit_disclosure_a(self): auto_present=True, error_msg="error", ) - await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + with async_mock.patch.object( + jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 + await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) async def test_verify_received_limit_disclosure_b(self): dif_proof = deepcopy(DIF_PRES) @@ -1610,7 +1619,11 @@ async def test_verify_received_limit_disclosure_b(self): auto_present=True, error_msg="error", ) - await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) + with async_mock.patch.object( + jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1 + await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) async def test_verify_received_pres_invalid_jsonpath(self): dif_proof = deepcopy(DIF_PRES) @@ -1956,6 +1969,9 @@ async def test_verify_received_pres_fail_schema_filter(self): ) with async_mock.patch.object( test_module.LOGGER, "error", async_mock.MagicMock() - ) as mock_log_err: + ) as mock_log_err, async_mock.patch.object( + jsonld, "expand", async_mock.MagicMock() + ) as mock_jsonld_expand: + mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) mock_log_err.assert_called_once() From 1ec26ec80d740877db5fa2cf2e99e7baf0196b1c Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 16 Nov 2021 18:49:31 -0800 Subject: [PATCH 5/7] updates Signed-off-by: Shaanjot Gill --- .../present_proof/dif/pres_exch_handler.py | 70 +++++++++---------- .../present_proof/v2_0/formats/dif/handler.py | 27 ++++++- .../present_proof/v2_0/formats/handler.py | 4 +- .../v2_0/formats/indy/handler.py | 4 +- .../protocols/present_proof/v2_0/manager.py | 23 ++++-- 5 files changed, 77 insertions(+), 51 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py index c3e5740c40..915a0f5c15 100644 --- a/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py @@ -430,7 +430,7 @@ def field_ids_for_is_holder(self, constraints: Constraints) -> Sequence[str]: reqd_field_ids = [] return reqd_field_ids for holder in constraints.holders: - if holder.directive == "required": + if holder.directive == "required" or holder.directive == "preferred": reqd_field_ids = set.union(reqd_field_ids, set(holder.field_ids)) return list(reqd_field_ids) @@ -1255,43 +1255,41 @@ async def create_vp( return vp else: vp = await create_presentation(credentials=filtered_creds_list) - vp["presentation_submission"] = submission_property.serialize() - if self.proof_type is BbsBlsSignature2020.signature_type: - vp["@context"].append(SECURITY_CONTEXT_BBS_URL) - async with self.profile.session() as session: - wallet = session.inject(BaseWallet) - issue_suite = await self._get_issue_suite( - wallet=wallet, - issuer_id=issuer_id, - ) - signed_vp = await sign_presentation( - presentation=vp, - suite=issue_suite, - challenge=challenge, - document_loader=document_loader, - ) - return signed_vp else: - vp = await create_presentation(credentials=applicable_creds_list) - vp["presentation_submission"] = submission_property.serialize() - if self.proof_type is BbsBlsSignature2020.signature_type: - vp["@context"].append(SECURITY_CONTEXT_BBS_URL) - if self.pres_signing_did: - async with self.profile.session() as session: - wallet = session.inject(BaseWallet) - issue_suite = await self._get_issue_suite( - wallet=wallet, - issuer_id=self.pres_signing_did, - ) - signed_vp = await sign_presentation( - presentation=vp, - suite=issue_suite, - challenge=challenge, - document_loader=document_loader, - ) - return signed_vp + if not self.pres_signing_did: + ( + issuer_id, + filtered_creds_list, + ) = await self.get_sign_key_credential_subject_id( + applicable_creds=applicable_creds + ) + if not issuer_id: + vp = await create_presentation(credentials=applicable_creds_list) + vp["presentation_submission"] = submission_property.serialize() + if self.proof_type is BbsBlsSignature2020.signature_type: + vp["@context"].append(SECURITY_CONTEXT_BBS_URL) + return vp + else: + vp = await create_presentation(credentials=filtered_creds_list) else: - return vp + issuer_id = self.pres_signing_did + vp = await create_presentation(credentials=applicable_creds_list) + vp["presentation_submission"] = submission_property.serialize() + if self.proof_type is BbsBlsSignature2020.signature_type: + vp["@context"].append(SECURITY_CONTEXT_BBS_URL) + async with self.profile.session() as session: + wallet = session.inject(BaseWallet) + issue_suite = await self._get_issue_suite( + wallet=wallet, + issuer_id=issuer_id, + ) + signed_vp = await sign_presentation( + presentation=vp, + suite=issue_suite, + challenge=challenge, + document_loader=document_loader, + ) + return signed_vp def check_if_cred_id_derived(self, id: str) -> bool: """Check if credential or credentialSubjet id is derived.""" diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py index 71335fb9e6..2f1b4f46c2 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/handler.py @@ -336,6 +336,25 @@ async def create_pres( credentials_list = credentials_list + vcrecord_list except StorageNotFoundError as err: raise V20PresFormatHandlerError(err) + except TypeError as err: + LOGGER.error(str(err)) + responder = self._profile.inject_or(BaseResponder) + if responder: + report = ProblemReport( + description={ + "en": ( + "Presentation request not properly formatted," + " TypeError raised on Holder agent." + ), + "code": ProblemReportReason.ABANDONED.value, + } + ) + if pres_ex_record.thread_id: + report.assign_thread_id(pres_ex_record.thread_id) + await responder.send_reply( + report, connection_id=pres_ex_record.connection_id + ) + return dif_handler = DIFPresExchHandler( self._profile, @@ -392,9 +411,7 @@ async def retrieve_uri_list_from_schema_filter( group_schema_uri_list.append(uri_list) return group_schema_uri_list - async def receive_pres( - self, message: V20Pres, pres_ex_record: V20PresExRecord - ) -> None: + async def receive_pres(self, message: V20Pres, pres_ex_record: V20PresExRecord): """Receive a presentation, from message in context on manager creation.""" dif_handler = DIFPresExchHandler(self._profile) dif_proof = message.attachment(DIFPresFormatHandler.format) @@ -406,6 +423,7 @@ async def receive_pres( ) try: await dif_handler.verify_received_pres(pd=pres_definition, pres=dif_proof) + return True except DIFPresExchError as err: LOGGER.error(str(err)) responder = self._profile.inject_or(BaseResponder) @@ -416,9 +434,12 @@ async def receive_pres( "code": ProblemReportReason.ABANDONED.value, } ) + if pres_ex_record.thread_id: + report.assign_thread_id(pres_ex_record.thread_id) await responder.send_reply( report, connection_id=pres_ex_record.connection_id ) + return False async def verify_pres(self, pres_ex_record: V20PresExRecord) -> V20PresExRecord: """ diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/handler.py index e70dd34c9c..96c01b83c2 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/handler.py @@ -79,9 +79,7 @@ async def create_pres( """Create a presentation.""" @abstractmethod - async def receive_pres( - self, message: V20Pres, pres_ex_record: V20PresExRecord - ) -> None: + async def receive_pres(self, message: V20Pres, pres_ex_record: V20PresExRecord): """Receive a presentation, from message in context on manager creation.""" @abstractmethod diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py index 229ed472d4..6158f3b96c 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py @@ -161,9 +161,7 @@ async def create_pres( ) return self.get_format_data(PRES_20, indy_proof) - async def receive_pres( - self, message: V20Pres, pres_ex_record: V20PresExRecord - ) -> None: + async def receive_pres(self, message: V20Pres, pres_ex_record: V20PresExRecord): """Receive a presentation and check for presented values vs. proposal request.""" def _check_proof_vs_proposal(): diff --git a/aries_cloudagent/protocols/present_proof/v2_0/manager.py b/aries_cloudagent/protocols/present_proof/v2_0/manager.py index b92ca2ecb6..688f4ca35f 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/manager.py @@ -259,12 +259,16 @@ async def create_pres( pres_exch_format = V20PresFormat.Format.get(format.format) if pres_exch_format: - pres_formats.append( - await pres_exch_format.handler(self._profile).create_pres( - pres_ex_record, - request_data, - ) + pres_tuple = await pres_exch_format.handler(self._profile).create_pres( + pres_ex_record, + request_data, ) + if pres_tuple: + pres_formats.append(pres_tuple) + else: + raise V20PresManagerError( + "Unable to create presentation. ProblemReport message sent" + ) if len(pres_formats) == 0: raise V20PresManagerError( "Unable to create presentation. No supported formats" @@ -322,10 +326,17 @@ async def receive_pres(self, message: V20Pres, conn_record: ConnRecord): pres_format = V20PresFormat.Format.get(format.format) if pres_format: - await pres_format.handler(self._profile).receive_pres( + receive_pres_return = await pres_format.handler( + self._profile + ).receive_pres( message, pres_ex_record, ) + if isinstance(receive_pres_return, bool) and not receive_pres_return: + raise V20PresManagerError( + "Unable to verify received presentation." + " ProblemReport message sent" + ) pres_ex_record.pres = message pres_ex_record.state = V20PresExRecord.STATE_PRESENTATION_RECEIVED From cc2557760ee6cdd43d6459b442918d652eae41b0 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 16 Nov 2021 20:00:13 -0800 Subject: [PATCH 6/7] test update Signed-off-by: Shaanjot Gill --- .../v2_0/formats/dif/tests/test_handler.py | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py index 09788ba581..4ac318c506 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/formats/dif/tests/test_handler.py @@ -8,6 +8,7 @@ from .......core.in_memory import InMemoryProfile from .......messaging.decorators.attach_decorator import AttachDecorator +from .......messaging.responder import MockResponder, BaseResponder from .......storage.vc_holder.base import VCHolder from .......storage.vc_holder.vc_record import VCRecord from .......vc.ld_proofs import ( @@ -260,6 +261,7 @@ async def setUp(self): # Set custom document loader self.context.injector.bind_instance(DocumentLoader, custom_document_loader) + self.context.injector.bind_instance(BaseResponder, MockResponder()) self.handler = DIFPresFormatHandler(self.profile) assert self.handler.profile @@ -1975,3 +1977,103 @@ async def test_verify_received_pres_fail_schema_filter(self): mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2 await self.handler.receive_pres(message=dif_pres, pres_ex_record=record) mock_log_err.assert_called_once() + + async def test_create_pres_catch_typeerror(self): + self.context.injector.bind_instance( + VCHolder, + async_mock.MagicMock( + search_credentials=async_mock.MagicMock(side_effect=TypeError) + ), + ) + test_pd = deepcopy(DIF_PRES_REQUEST_B) + dif_pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_json(test_pd, ident="dif") + ], + ) + record = V20PresExRecord( + pres_ex_id="pxid", + thread_id="thid", + connection_id="conn_id", + initiator="init", + role="role", + state="state", + pres_request=dif_pres_request, + verified="false", + auto_present=True, + error_msg="error", + ) + await self.handler.create_pres(record) + + async def test_create_pres_catch_diferror(self): + cred_list = [ + VCRecord( + contexts=[ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1", + ], + expanded_types=[ + "https://www.w3.org/2018/credentials#VerifiableCredential", + "https://example.org/examples#UniversityDegreeCredential", + ], + issuer_id="did:example:489398593", + subject_ids=[ + "did:sov:WgWxqztrNooG92RXvxSTWv", + ], + proof_types=["Ed25519Signature2018"], + schema_ids=["https://example.org/examples/degree.json"], + cred_value={"...", "..."}, + given_id="http://example.edu/credentials/3732", + cred_tags={"some": "tag"}, + record_id="test1", + ) + ] + self.context.injector.bind_instance( + VCHolder, + async_mock.MagicMock( + search_credentials=async_mock.MagicMock( + return_value=async_mock.MagicMock( + fetch=async_mock.CoroutineMock(return_value=cred_list) + ) + ) + ), + ) + test_pd = deepcopy(DIF_PRES_REQUEST_B) + dif_pres_request = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_json(test_pd, ident="dif") + ], + ) + record = V20PresExRecord( + pres_ex_id="pxid", + thread_id="thid", + connection_id="conn_id", + initiator="init", + role="role", + state="state", + pres_request=dif_pres_request, + verified="false", + auto_present=True, + error_msg="error", + ) + with async_mock.patch.object( + DIFPresExchHandler, "create_vp", async_mock.MagicMock() + ) as mock_create_vp: + mock_create_vp.side_effect = DIFPresExchError("TEST") + await self.handler.create_pres(record) From 642375a1a6a64c2a5d8ca49f5f3dc990d95da984 Mon Sep 17 00:00:00 2001 From: Shaanjot Gill Date: Tue, 16 Nov 2021 21:02:51 -0800 Subject: [PATCH 7/7] test update Signed-off-by: Shaanjot Gill --- .../present_proof/v2_0/tests/test_manager.py | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py index 045ab829d3..3cd33d49a0 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/tests/test_manager.py @@ -23,6 +23,11 @@ from .. import manager as test_module from ..formats.handler import V20PresFormatHandlerError +from ..formats.dif.handler import DIFPresFormatHandler +from ..formats.dif.tests.test_handler import ( + DIF_PRES_REQUEST_B as DIF_PRES_REQ, + DIF_PRES, +) from ..formats.indy import handler as test_indy_handler from ..manager import V20PresManager, V20PresManagerError from ..message_types import ( @@ -603,6 +608,86 @@ async def test_create_pres_no_format(self): ) assert "No supported formats" in str(context.exception) + async def test_create_pres_catch_diferror(self): + px_rec = V20PresExRecord( + pres_request=V20PresRequest( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_json(DIF_PRES_REQ, ident="dif") + ], + ).serialize(), + ) + with async_mock.patch.object( + DIFPresFormatHandler, "create_pres", autospec=True + ) as mock_create_pres: + mock_create_pres.return_value = None + with self.assertRaises(V20PresManagerError) as context: + await self.manager.create_pres( + pres_ex_record=px_rec, + request_data={}, + comment="test", + ) + assert "Unable to create presentation. ProblemReport message sent" in str( + context.exception + ) + + async def test_receive_pres_catch_diferror(self): + connection_record = async_mock.MagicMock(connection_id=CONN_ID) + pres_x = V20Pres( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20][V20PresFormat.Format.DIF.api], + ) + ], + presentations_attach=[ + AttachDecorator.data_json( + mapping=DIF_PRES, + ident="dif", + ) + ], + ) + pres_req = V20PresRequest( + formats=[ + V20PresFormat( + attach_id="dif", + format_=ATTACHMENT_FORMAT[PRES_20_REQUEST][ + V20PresFormat.Format.DIF.api + ], + ) + ], + request_presentations_attach=[ + AttachDecorator.data_json(DIF_PRES_REQ, ident="dif") + ], + ) + px_rec = V20PresExRecord( + pres_request=pres_req.serialize(), + pres=pres_x.serialize(), + ) + with async_mock.patch.object( + DIFPresFormatHandler, "receive_pres", autospec=True + ) as mock_receive_pres, async_mock.patch.object( + V20PresExRecord, "retrieve_by_tag_filter", autospec=True + ) as retrieve_ex: + mock_receive_pres.return_value = False + retrieve_ex.side_effect = [ + StorageNotFoundError("no such record"), # cover out-of-band + px_rec, + ] + with self.assertRaises(V20PresManagerError) as context: + await self.manager.receive_pres( + pres_x, + connection_record, + ) + assert "Unable to verify received presentation." in str(context.exception) + async def test_create_exchange_for_request(self): pres_req = V20PresRequest( comment="Test",