From 5a4ad8ef0bc954f277e3ea6aeeac96b64fcf3a17 Mon Sep 17 00:00:00 2001 From: sklump Date: Thu, 3 Sep 2020 13:07:06 +0000 Subject: [PATCH 1/2] when >1 cred in wallet matching proof req, allow for names in requested attrs Signed-off-by: sklump --- .../test_presentation_request_handler.py | 3 +- .../present_proof/v1_0/tests/test_manager.py | 155 ++++++++++++++++-- .../protocols/present_proof/v1_0/util/indy.py | 50 +++--- 3 files changed, 166 insertions(+), 42 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py index 9358022bf9..0b77ddf314 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py @@ -641,7 +641,7 @@ async def test_called_auto_present_bait_and_switch(self): handler, "BaseHolder", autospec=True ) as mock_holder: - mock_holder.get_credentials_for_presentation_request_by_referent = async_mock.CoroutineMock( + by_reft = async_mock.CoroutineMock( return_value=[ { "cred_info": { @@ -669,6 +669,7 @@ async def test_called_auto_present_bait_and_switch(self): }, ] ) + mock_holder.get_credentials_for_presentation_request_by_referent = by_reft request_context.inject = async_mock.CoroutineMock(return_value=mock_holder) mock_pres_ex_rec.return_value = px_rec_instance diff --git a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py index 46a4c153e9..56e05ab82b 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py @@ -52,6 +52,28 @@ ) ], ) +PRES_PREVIEW_NAMES = PresentationPreview( + attributes=[ + PresAttrSpec( + name="player", + cred_def_id=CD_ID, + value="Richie Knucklez", + referent="0" + ), + PresAttrSpec( + name="screenCapture", + cred_def_id=CD_ID, + mime_type="image/png", + value="aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", + referent="0" + ), + ], + predicates=[ + PresPredSpec( + name="highScore", cred_def_id=CD_ID, predicate=">=", threshold=1000000 + ) + ], +) PROOF_REQ_NAME = "name" PROOF_REQ_VERSION = "1.0" PROOF_REQ_NONCE = "12345" @@ -453,6 +475,17 @@ async def test_create_presentation_no_revocation(self): self.context.connection_record = async_mock.MagicMock() self.context.connection_record.connection_id = CONN_ID + Ledger = async_mock.MagicMock(BaseLedger, autospec=True) + self.ledger = Ledger() + self.ledger.get_schema = async_mock.CoroutineMock( + return_value=async_mock.MagicMock() + ) + self.ledger.get_credential_definition = async_mock.CoroutineMock( + return_value={"value": {"revocation": None}} + ) + self.context.injector.clear_binding(BaseLedger) + self.context.injector.bind_instance(BaseLedger, self.ledger) + exchange_in = V10PresentationExchange() indy_proof_req = await PRES_PREVIEW.indy_proof_request( name=PROOF_REQ_NAME, @@ -493,31 +526,14 @@ async def test_create_presentation_no_revocation(self): ) ) self.holder.create_presentation = async_mock.CoroutineMock(return_value="{}") - self.holder.create_revocation_state = async_mock.CoroutineMock( - return_value=json.dumps( - { - "witness": {"omega": "1 ..."}, - "rev_reg": {"accum": "21 ..."}, - "timestamp": NOW, - } - ) - ) self.context.injector.clear_binding(BaseHolder) self.context.injector.bind_instance(BaseHolder, self.holder) - more_magic_rr = async_mock.MagicMock( - get_or_fetch_local_tails_path=async_mock.CoroutineMock( - return_value="/tmp/sample/tails/path" - ) - ) with async_mock.patch.object( V10PresentationExchange, "save", autospec=True ) as save_ex, async_mock.patch.object( test_module, "AttachDecorator", autospec=True - ) as mock_attach_decorator, async_mock.patch.object( - test_module, "RevocationRegistry", autospec=True - ) as mock_rr: - mock_rr.from_definition = async_mock.MagicMock(return_value=more_magic_rr) + ) as mock_attach_decorator: mock_attach_decorator.from_indy_dict = async_mock.MagicMock( return_value=mock_attach_decorator @@ -609,6 +625,109 @@ async def test_create_presentation_bad_revoc_state(self): with self.assertRaises(test_module.HolderError): await self.manager.create_presentation(exchange_in, req_creds) + async def test_create_presentation_multi_matching_proposal_creds_names(self): + self.context.connection_record = async_mock.MagicMock() + self.context.connection_record.connection_id = CONN_ID + + exchange_in = V10PresentationExchange() + indy_proof_req = await PRES_PREVIEW_NAMES.indy_proof_request( + name=PROOF_REQ_NAME, + version=PROOF_REQ_VERSION, + nonce=PROOF_REQ_NONCE, + ledger=await self.context.inject(BaseLedger, required=False), + ) + + exchange_in.presentation_request = indy_proof_req + request = async_mock.MagicMock() + request.indy_proof_request = async_mock.MagicMock() + request._thread_id = "dummy" + self.context.message = request + + Holder = async_mock.MagicMock(IndyHolder, autospec=True) + self.holder = Holder() + get_creds = async_mock.CoroutineMock( + return_value=( + { + "cred_info": { + "referent": "dummy_reft_0", + "cred_def_id": CD_ID, + "attrs": { + "player": "Richie Knucklez", + "screenCapture": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", + "highScore": "1234560", + }, + } + }, + { + "cred_info": { + "referent": "dummy_reft_1", + "cred_def_id": CD_ID, + "attrs": { + "player": "Richie Knucklez", + "screenCapture": "aW1hZ2luZSBhbm90aGVyIHNjcmVlbiBjYXB0dXJl", + "highScore": "1515880", + }, + } + } + ) + ) + self.holder.get_credentials_for_presentation_request_by_referent = get_creds + self.holder.get_credential = async_mock.CoroutineMock( + return_value=json.dumps( + { + "schema_id": S_ID, + "cred_def_id": CD_ID, + "rev_reg_id": RR_ID, + "cred_rev_id": 1, + } + ) + ) + self.holder.create_presentation = async_mock.CoroutineMock(return_value="{}") + self.holder.create_revocation_state = async_mock.CoroutineMock( + return_value=json.dumps( + { + "witness": {"omega": "1 ..."}, + "rev_reg": {"accum": "21 ..."}, + "timestamp": NOW, + } + ) + ) + self.context.injector.clear_binding(BaseHolder) + self.context.injector.bind_instance(BaseHolder, self.holder) + + more_magic_rr = async_mock.MagicMock( + get_or_fetch_local_tails_path=async_mock.CoroutineMock( + return_value="/tmp/sample/tails/path" + ) + ) + with async_mock.patch.object( + V10PresentationExchange, "save", autospec=True + ) as save_ex, async_mock.patch.object( + test_module, "AttachDecorator", autospec=True + ) as mock_attach_decorator, async_mock.patch.object( + test_module, "RevocationRegistry", autospec=True + ) as mock_rr: + mock_rr.from_definition = async_mock.MagicMock(return_value=more_magic_rr) + + mock_attach_decorator.from_indy_dict = async_mock.MagicMock( + return_value=mock_attach_decorator + ) + + req_creds = await indy_proof_req_preview2indy_requested_creds( + indy_proof_req, + preview=PRES_PREVIEW_NAMES, + holder=self.holder + ) + assert not req_creds["self_attested_attributes"] + assert len(req_creds["requested_attributes"]) == 1 + assert len(req_creds["requested_predicates"]) == 1 + + (exchange_out, pres_msg) = await self.manager.create_presentation( + exchange_in, req_creds + ) + save_ex.assert_called_once() + assert exchange_out.state == V10PresentationExchange.STATE_PRESENTATION_SENT + async def test_no_matching_creds_for_proof_req(self): exchange_in = V10PresentationExchange() indy_proof_req = await PRES_PREVIEW.indy_proof_request( diff --git a/aries_cloudagent/protocols/present_proof/v1_0/util/indy.py b/aries_cloudagent/protocols/present_proof/v1_0/util/indy.py index 3ab4f73446..ce2d0cd740 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/util/indy.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/util/indy.py @@ -1,13 +1,15 @@ """Utilities for dealing with indy conventions.""" - from .....holder.base import BaseHolder from ..messages.inner.presentation_preview import PresentationPreview async def indy_proof_req_preview2indy_requested_creds( - indy_proof_request: dict, preview: PresentationPreview = None, *, holder: BaseHolder + indy_proof_request: dict, + preview: PresentationPreview = None, + *, + holder: BaseHolder ): """ Build indy requested-credentials structure. @@ -46,30 +48,32 @@ async def indy_proof_req_preview2indy_requested_creds( # match returned creds against any preview values if len(credentials) == 1: cred_match = credentials[0] - else: - if preview: - for cred in sorted( - credentials, key=lambda c: c["cred_info"]["referent"] - ): - name = indy_proof_request["requested_attributes"][referent]["name"] - value = cred["cred_info"]["attrs"][name] - if preview.has_attr_spec( + elif preview: + reft = indy_proof_request["requested_attributes"][referent] + names = [reft["name"]] if "name" in reft else reft.get("names") + for cred in sorted( + credentials, key=lambda c: c["cred_info"]["referent"] + ): + if all( + preview.has_attr_spec( cred_def_id=cred["cred_info"]["cred_def_id"], name=name, - value=value, - ): - cred_match = cred - break - else: - raise ValueError( - f"Could not automatically construct presentation for " - + f"presentation request {indy_proof_request['name']}" - + f":{indy_proof_request['version']} because referent " - + f"{referent} did not produce any credentials matching " - + f"proposed preview." - ) + value=cred["cred_info"]["attrs"][name], + ) for name in names + ): + cred_match = cred + break else: - cred_match = min(credentials, key=lambda c: c["cred_info"]["referent"]) + raise ValueError( + f"Could not automatically construct presentation for " + + f"presentation request {indy_proof_request['name']}" + + f":{indy_proof_request['version']} because referent " + + f"{referent} did not produce any credentials matching " + + f"proposed preview." + ) + else: + cred_match = min(credentials, key=lambda c: c["cred_info"]["referent"]) + if "restrictions" in indy_proof_request["requested_attributes"][referent]: req_creds["requested_attributes"][referent] = { "cred_id": cred_match["cred_info"]["referent"], From 91bfe84214939b7844ce13f9f2f042841cc20de0 Mon Sep 17 00:00:00 2001 From: sklump Date: Thu, 3 Sep 2020 14:07:58 +0000 Subject: [PATCH 2/2] black tweak Signed-off-by: sklump --- .../present_proof/v1_0/tests/test_manager.py | 13 ++++--------- .../protocols/present_proof/v1_0/util/indy.py | 12 ++++-------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py index 56e05ab82b..425c1f3165 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py @@ -55,17 +55,14 @@ PRES_PREVIEW_NAMES = PresentationPreview( attributes=[ PresAttrSpec( - name="player", - cred_def_id=CD_ID, - value="Richie Knucklez", - referent="0" + name="player", cred_def_id=CD_ID, value="Richie Knucklez", referent="0" ), PresAttrSpec( name="screenCapture", cred_def_id=CD_ID, mime_type="image/png", value="aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - referent="0" + referent="0", ), ], predicates=[ @@ -668,7 +665,7 @@ async def test_create_presentation_multi_matching_proposal_creds_names(self): "highScore": "1515880", }, } - } + }, ) ) self.holder.get_credentials_for_presentation_request_by_referent = get_creds @@ -714,9 +711,7 @@ async def test_create_presentation_multi_matching_proposal_creds_names(self): ) req_creds = await indy_proof_req_preview2indy_requested_creds( - indy_proof_req, - preview=PRES_PREVIEW_NAMES, - holder=self.holder + indy_proof_req, preview=PRES_PREVIEW_NAMES, holder=self.holder ) assert not req_creds["self_attested_attributes"] assert len(req_creds["requested_attributes"]) == 1 diff --git a/aries_cloudagent/protocols/present_proof/v1_0/util/indy.py b/aries_cloudagent/protocols/present_proof/v1_0/util/indy.py index ce2d0cd740..8bedaf2b55 100644 --- a/aries_cloudagent/protocols/present_proof/v1_0/util/indy.py +++ b/aries_cloudagent/protocols/present_proof/v1_0/util/indy.py @@ -6,10 +6,7 @@ async def indy_proof_req_preview2indy_requested_creds( - indy_proof_request: dict, - preview: PresentationPreview = None, - *, - holder: BaseHolder + indy_proof_request: dict, preview: PresentationPreview = None, *, holder: BaseHolder ): """ Build indy requested-credentials structure. @@ -51,15 +48,14 @@ async def indy_proof_req_preview2indy_requested_creds( elif preview: reft = indy_proof_request["requested_attributes"][referent] names = [reft["name"]] if "name" in reft else reft.get("names") - for cred in sorted( - credentials, key=lambda c: c["cred_info"]["referent"] - ): + for cred in sorted(credentials, key=lambda c: c["cred_info"]["referent"]): if all( preview.has_attr_spec( cred_def_id=cred["cred_info"]["cred_def_id"], name=name, value=cred["cred_info"]["attrs"][name], - ) for name in names + ) + for name in names ): cred_match = cred break