diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/__init__.py b/aries_cloudagent/protocols/issue_credential/v1_0/__init__.py deleted file mode 100644 index e621c27f9e..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/__init__.py +++ /dev/null @@ -1,60 +0,0 @@ -from typing import Coroutine, Union - -from ....connections.models.conn_record import ConnRecord -from ....core.error import BaseError -from .messages.credential_problem_report import ( - CredentialProblemReport, - ProblemReportReason, -) -from .models.credential_exchange import V10CredentialExchange - - -def problem_report_for_record( - record: Union[ConnRecord, V10CredentialExchange], - desc_en: str, -) -> CredentialProblemReport: - """Create problem report for record. - - Args: - record: connection or exchange record - desc_en: description text to include in problem report - - """ - result = CredentialProblemReport( - description={ - "en": desc_en, - "code": ProblemReportReason.ISSUANCE_ABANDONED.value, - }, - ) - if record: - thid = getattr(record, "thread_id", None) - if thid: - result.assign_thread_id(thid) - - return result - - -async def report_problem( - err: BaseError, - desc_en: str, - http_error_class, - record: Union[ConnRecord, V10CredentialExchange], - outbound_handler: Coroutine, -): - """Send problem report response and raise corresponding HTTP error. - - Args: - err: error for internal diagnostics - desc_en: description text to include in problem report (response) - http_error_class: HTTP error to raise - record: record to cite by thread in problem report - outbound_handler: outbound message handler - - """ - if record: - await outbound_handler( - problem_report_for_record(record, desc_en), - connection_id=record.connection_id, - ) - - raise http_error_class(reason=err.roll_up) from err diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/controller.py b/aries_cloudagent/protocols/issue_credential/v1_0/controller.py deleted file mode 100644 index 290a8d2de9..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/controller.py +++ /dev/null @@ -1,17 +0,0 @@ -"""Protocol controller for issue credential v1_0.""" - -from typing import Sequence - -PARTICIPATE_VC_INTERACTION = "aries.vc" -ISSUE_VC = "aries.vc.issue" - - -class Controller: - """Issue credential v1_0 protocol controller.""" - - def __init__(self, protocol: str): - """Initialize the controller.""" - - def determine_goal_codes(self) -> Sequence[str]: - """Return defined goal_codes.""" - return [PARTICIPATE_VC_INTERACTION, ISSUE_VC] diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/__init__.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_ack_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_ack_handler.py deleted file mode 100644 index 386eb28030..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_ack_handler.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Credential ack message handler.""" - -from .....core.oob_processor import OobMessageProcessor -from .....messaging.base_handler import BaseHandler, HandlerException -from .....messaging.request_context import RequestContext -from .....messaging.responder import BaseResponder -from .....utils.tracing import get_timer, trace_event -from ..manager import CredentialManager -from ..messages.credential_ack import CredentialAck - - -class CredentialAckHandler(BaseHandler): - """Message handler class for credential acks.""" - - async def handle(self, context: RequestContext, responder: BaseResponder): - """Message handler logic for credential acks. - - Args: - context: request context - responder: responder callback - """ - r_time = get_timer() - - self._logger.debug("CredentialAckHandler called with context %s", context) - assert isinstance(context.message, CredentialAck) - self._logger.info( - "Received credential ack message: %s", - context.message.serialize(as_string=True), - ) - - # If connection is present it must be ready for use - if context.connection_record and not context.connection_ready: - raise HandlerException("Connection used for credential ack not ready") - - # Find associated oob record - oob_processor = context.inject(OobMessageProcessor) - oob_record = await oob_processor.find_oob_record_for_inbound_message(context) - - # Either connection or oob context must be present - if not context.connection_record and not oob_record: - raise HandlerException( - "No connection or associated connectionless exchange found for credential" - " ack" - ) - - credential_manager = CredentialManager(context.profile) - await credential_manager.receive_credential_ack( - context.message, - ( - context.connection_record.connection_id - if context.connection_record - else None - ), - ) - - trace_event( - context.settings, - context.message, - outcome="CredentialAckHandler.handle.END", - perf_counter=r_time, - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py deleted file mode 100644 index 109ce07f85..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_issue_handler.py +++ /dev/null @@ -1,101 +0,0 @@ -"""Credential issue message handler.""" - -from .....core.oob_processor import OobMessageProcessor -from .....indy.holder import IndyHolderError -from .....messaging.base_handler import BaseHandler, HandlerException -from .....messaging.models.base import BaseModelError -from .....messaging.request_context import RequestContext -from .....messaging.responder import BaseResponder -from .....storage.error import StorageError -from .....utils.tracing import get_timer, trace_event -from .. import problem_report_for_record -from ..manager import CredentialManager, CredentialManagerError -from ..messages.credential_issue import CredentialIssue -from ..messages.credential_problem_report import ProblemReportReason - - -class CredentialIssueHandler(BaseHandler): - """Message handler class for credential offers.""" - - async def handle(self, context: RequestContext, responder: BaseResponder): - """Message handler logic for credential offers. - - Args: - context: request context - responder: responder callback - - """ - r_time = get_timer() - profile = context.profile - self._logger.debug("CredentialHandler called with context %s", context) - assert isinstance(context.message, CredentialIssue) - self._logger.info( - "Received credential message: %s", context.message.serialize(as_string=True) - ) - - # If connection is present it must be ready for use - if context.connection_record and not context.connection_ready: - raise HandlerException("Connection used for credential not ready") - - # Find associated oob record - oob_processor = context.inject(OobMessageProcessor) - oob_record = await oob_processor.find_oob_record_for_inbound_message(context) - - # Either connection or oob context must be present - if not context.connection_record and not oob_record: - raise HandlerException( - "No connection or associated connectionless exchange found for credential" - ) - - credential_manager = CredentialManager(profile) - cred_ex_record = await credential_manager.receive_credential( - context.message, - ( - context.connection_record.connection_id - if context.connection_record - else None - ), - ) # mgr only finds, saves record: on exception, saving state null is hopeless - - r_time = trace_event( - context.settings, - context.message, - outcome="CredentialIssueHandler.handle.END", - perf_counter=r_time, - ) - - # Automatically move to next state if flag is set - if cred_ex_record and context.settings.get("debug.auto_store_credential"): - try: - cred_ex_record = await credential_manager.store_credential(cred_ex_record) - except ( - BaseModelError, - CredentialManagerError, - IndyHolderError, - StorageError, - ) as err: - # treat failure to store as mangled on receipt hence protocol error - self._logger.exception("Error storing issued credential") - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state( - session, - reason=err.roll_up, # us: be specific - ) - await responder.send_reply( - problem_report_for_record( - cred_ex_record, - ProblemReportReason.ISSUANCE_ABANDONED.value, # them: vague - ) - ) - - (_, credential_ack_message) = await credential_manager.send_credential_ack( - cred_ex_record - ) - - trace_event( - context.settings, - credential_ack_message, - outcome="CredentialIssueHandler.handle.STORE", - perf_counter=r_time, - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py deleted file mode 100644 index 2be8c2478c..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_offer_handler.py +++ /dev/null @@ -1,114 +0,0 @@ -"""Credential offer message handler.""" - -from .....core.oob_processor import OobMessageProcessor -from .....indy.holder import IndyHolderError -from .....ledger.error import LedgerError -from .....messaging.base_handler import BaseHandler, HandlerException -from .....messaging.models.base import BaseModelError -from .....messaging.request_context import RequestContext -from .....messaging.responder import BaseResponder -from .....storage.error import StorageError -from .....utils.tracing import get_timer, trace_event -from .....wallet.util import default_did_from_verkey -from .. import problem_report_for_record -from ..manager import CredentialManager, CredentialManagerError -from ..messages.credential_offer import CredentialOffer -from ..messages.credential_problem_report import ProblemReportReason - - -class CredentialOfferHandler(BaseHandler): - """Message handler class for credential offers.""" - - async def handle(self, context: RequestContext, responder: BaseResponder): - """Message handler logic for credential offers. - - Args: - context: request context - responder: responder callback - - """ - r_time = get_timer() - profile = context.profile - self._logger.debug("CredentialOfferHandler called with context %s", context) - assert isinstance(context.message, CredentialOffer) - self._logger.info( - "Received credential offer message: %s", - context.message.serialize(as_string=True), - ) - - # If connection is present it must be ready for use - if context.connection_record and not context.connection_ready: - raise HandlerException("Connection used for credential offer not ready") - - # Find associated oob record - oob_processor = context.inject(OobMessageProcessor) - oob_record = await oob_processor.find_oob_record_for_inbound_message(context) - - # Either connection or oob context must be present - if not context.connection_record and not oob_record: - raise HandlerException( - "No connection or associated connectionless exchange found for credential" - " offer" - ) - - connection_id = ( - context.connection_record.connection_id if context.connection_record else None - ) - - credential_manager = CredentialManager(profile) - cred_ex_record = await credential_manager.receive_offer( - context.message, connection_id - ) # mgr only finds, saves record: on exception, saving state null is hopeless - - r_time = trace_event( - context.settings, - context.message, - outcome="CredentialOfferHandler.handle.END", - perf_counter=r_time, - ) - - if context.connection_record: - holder_did = context.connection_record.my_did - else: - # Transform recipient key into did - holder_did = default_did_from_verkey(oob_record.our_recipient_key) - - # If auto respond is turned on, automatically reply with credential request - if cred_ex_record and context.settings.get("debug.auto_respond_credential_offer"): - credential_request_message = None - try: - ( - _, - credential_request_message, - ) = await credential_manager.create_request( - cred_ex_record=cred_ex_record, - holder_did=holder_did, - ) - await responder.send_reply(credential_request_message) - except ( - BaseModelError, - CredentialManagerError, - IndyHolderError, - LedgerError, - StorageError, - ) as err: - self._logger.exception("Error responding to credential offer") - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state( - session, - reason=err.roll_up, # us: be specific - ) - await responder.send_reply( - problem_report_for_record( - cred_ex_record, - ProblemReportReason.ISSUANCE_ABANDONED.value, # them: vague - ) - ) - - trace_event( - context.settings, - credential_request_message, - outcome="CredentialOfferHandler.handle.REQUEST", - perf_counter=r_time, - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_problem_report_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_problem_report_handler.py deleted file mode 100644 index 2582b11b9b..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_problem_report_handler.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Credential problem report message handler.""" - -from .....messaging.base_handler import BaseHandler, HandlerException -from .....messaging.request_context import RequestContext -from .....messaging.responder import BaseResponder -from .....storage.error import StorageError, StorageNotFoundError -from ..manager import CredentialManager -from ..messages.credential_problem_report import CredentialProblemReport - - -class CredentialProblemReportHandler(BaseHandler): - """Message handler class for problem reports.""" - - async def handle(self, context: RequestContext, responder: BaseResponder): - """Message handler logic for problem reports. - - Args: - context: request context - responder: responder callback - """ - self._logger.debug( - "Issue-credential v1.0 problem report handler called with context %s", - context, - ) - assert isinstance(context.message, CredentialProblemReport) - - # If connection is present it must be ready for use - if context.connection_record and not context.connection_ready: - raise HandlerException( - "Connection used for credential problem report not ready" - ) - elif not context.connection_record: - raise HandlerException( - "Connectionless not supported for credential problem report" - ) - - credential_manager = CredentialManager(context.profile) - try: - await credential_manager.receive_problem_report( - context.message, - context.connection_record.connection_id, - ) - except (StorageError, StorageNotFoundError): - self._logger.exception( - "Error processing issue-credential v1.0 problem report message" - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py deleted file mode 100644 index bbae16aa04..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_proposal_handler.py +++ /dev/null @@ -1,95 +0,0 @@ -"""Credential proposal message handler.""" - -from .....indy.issuer import IndyIssuerError -from .....ledger.error import LedgerError -from .....messaging.base_handler import BaseHandler, HandlerException -from .....messaging.models.base import BaseModelError -from .....messaging.request_context import RequestContext -from .....messaging.responder import BaseResponder -from .....storage.error import StorageError -from .....utils.tracing import get_timer, trace_event -from .. import problem_report_for_record -from ..manager import CredentialManager, CredentialManagerError -from ..messages.credential_problem_report import ProblemReportReason -from ..messages.credential_proposal import CredentialProposal - - -class CredentialProposalHandler(BaseHandler): - """Message handler class for credential proposals.""" - - async def handle(self, context: RequestContext, responder: BaseResponder): - """Message handler logic for credential proposals. - - Args: - context: proposal context - responder: responder callback - - """ - r_time = get_timer() - profile = context.profile - - self._logger.debug("CredentialProposalHandler called with context %s", context) - assert isinstance(context.message, CredentialProposal) - self._logger.info( - "Received credential proposal message: %s", - context.message.serialize(as_string=True), - ) - - # If connection is present it must be ready for use - if context.connection_record and not context.connection_ready: - raise HandlerException("Connection used for credential proposal not ready") - elif not context.connection_record: - raise HandlerException("Connectionless not supported for credential proposal") - - credential_manager = CredentialManager(profile) - cred_ex_record = await credential_manager.receive_proposal( - context.message, context.connection_record.connection_id - ) # mgr only finds, saves record: on exception, saving state null is hopeless - - r_time = trace_event( - context.settings, - context.message, - outcome="CredentialProposalHandler.handle.END", - perf_counter=r_time, - ) - - # If auto_offer is enabled, respond immediately with offer - if cred_ex_record.auto_offer: - credential_offer_message = None - try: - ( - cred_ex_record, - credential_offer_message, - ) = await credential_manager.create_offer( - cred_ex_record, - counter_proposal=None, - comment=context.message.comment, - ) - await responder.send_reply(credential_offer_message) - except ( - BaseModelError, - CredentialManagerError, - IndyIssuerError, - LedgerError, - StorageError, - ) as err: - self._logger.exception("Error responding to credential proposal") - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state( - session, - reason=err.roll_up, # us: be specific - ) - await responder.send_reply( - problem_report_for_record( - cred_ex_record, - ProblemReportReason.ISSUANCE_ABANDONED.value, # them: vague - ) - ) - - trace_event( - context.settings, - credential_offer_message, - outcome="CredentialProposalHandler.handle.OFFER", - perf_counter=r_time, - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py deleted file mode 100644 index 5b3fcc9d48..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/credential_request_handler.py +++ /dev/null @@ -1,125 +0,0 @@ -"""Credential request message handler.""" - -from .....core.oob_processor import OobMessageProcessor -from .....indy.issuer import IndyIssuerError -from .....ledger.error import LedgerError -from .....messaging.base_handler import BaseHandler, HandlerException -from .....messaging.models.base import BaseModelError -from .....messaging.request_context import RequestContext -from .....messaging.responder import BaseResponder -from .....storage.error import StorageError, StorageNotFoundError -from .....utils.tracing import get_timer, trace_event -from .. import problem_report_for_record -from ..manager import CredentialManager, CredentialManagerError -from ..messages.credential_problem_report import ProblemReportReason -from ..messages.credential_request import CredentialRequest - - -class CredentialRequestHandler(BaseHandler): - """Message handler class for credential requests.""" - - async def handle(self, context: RequestContext, responder: BaseResponder): - """Message handler logic for credential requests. - - Args: - context: request context - responder: responder callback - - """ - r_time = get_timer() - profile = context.profile - self._logger.debug("CredentialRequestHandler called with context %s", context) - assert isinstance(context.message, CredentialRequest) - self._logger.info( - "Received credential request message: %s", - context.message.serialize(as_string=True), - ) - - # If connection is present it must be ready for use - if context.connection_record and not context.connection_ready: - raise HandlerException("Connection used for credential request not ready") - - # Find associated oob record. If the credential offer was created as an oob - # attachment the presentation exchange record won't have a connection id (yet) - oob_processor = context.inject(OobMessageProcessor) - oob_record = await oob_processor.find_oob_record_for_inbound_message(context) - - # Either connection or oob context must be present - if not context.connection_record and not oob_record: - raise HandlerException( - "No connection or associated connectionless exchange found for credential" - " request" - ) - - credential_manager = CredentialManager(profile) - try: - cred_ex_record = await credential_manager.receive_request( - context.message, context.connection_record, oob_record - ) # mgr only finds, saves record: on exception, saving state null is hopeless - except StorageNotFoundError: - # issue a problem report... - cred_ex_record = None - thread_id = context.message._thread_id - await responder.send_reply( - problem_report_for_record( - None, - ProblemReportReason.RECORD_NOT_FOUND.value, - thread_id=thread_id, - ) - ) - r_time = trace_event( - context.settings, - context.message, - outcome="CredentialRequestHandler.handle.END", - perf_counter=r_time, - ) - - # If auto_issue is enabled, respond immediately - if cred_ex_record and cred_ex_record.auto_issue: - if ( - cred_ex_record.credential_proposal_dict - and cred_ex_record.credential_proposal_dict.credential_proposal - ): - credential_issue_message = None - try: - ( - cred_ex_record, - credential_issue_message, - ) = await credential_manager.issue_credential( - cred_ex_record=cred_ex_record, - comment=context.message.comment, - ) - await responder.send_reply(credential_issue_message) - except ( - BaseModelError, - CredentialManagerError, - IndyIssuerError, - LedgerError, - StorageError, - ) as err: - self._logger.exception("Error responding to credential request") - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state( - session, - reason=err.roll_up, # us: be specific - ) - await responder.send_reply( # them: be vague - problem_report_for_record( - cred_ex_record, - ProblemReportReason.ISSUANCE_ABANDONED.value, - ) - ) - - trace_event( - context.settings, - credential_issue_message, - outcome="CredentialRequestHandler.issue.END", - perf_counter=r_time, - ) - else: - self._logger.warning( - "Operation set for auto-issue but credential exchange record " - f"{cred_ex_record.credential_exchange_id} " - "has no attribute values" - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/__init__.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_ack_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_ack_handler.py deleted file mode 100644 index 840ea3823b..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_ack_handler.py +++ /dev/null @@ -1,90 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......core.oob_processor import OobMessageProcessor -from ......messaging.request_context import RequestContext -from ......messaging.responder import MockResponder -from ......transport.inbound.receipt import MessageReceipt -from ...messages.credential_ack import CredentialAck -from .. import credential_ack_handler as test_module - - -class TestCredentialAckHandler(IsolatedAsyncioTestCase): - async def test_called(self): - request_context = RequestContext.test_context() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_credential_ack = mock.CoroutineMock() - request_context.message = CredentialAck() - request_context.connection_ready = True - handler = test_module.CredentialAckHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_credential_ack.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - assert not responder.messages - - async def test_called_not_ready(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_credential_ack = mock.CoroutineMock() - request_context.message = CredentialAck() - request_context.connection_ready = False - handler = test_module.CredentialAckHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert err.exception.message == "Connection used for credential ack not ready" - - async def test_called_no_connection_no_oob(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - # No oob record found - return_value=None - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_credential_ack = mock.CoroutineMock() - request_context.message = CredentialAck() - request_context.connection_ready = False - handler = test_module.CredentialAckHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "No connection or associated connectionless exchange found for credential ack" - ) - - assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_issue_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_issue_handler.py deleted file mode 100644 index 11ca5a209b..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_issue_handler.py +++ /dev/null @@ -1,176 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......core.oob_processor import OobMessageProcessor -from ......messaging.request_context import RequestContext -from ......messaging.responder import MockResponder -from ......transport.inbound.receipt import MessageReceipt -from ...messages.credential_issue import CredentialIssue -from .. import credential_issue_handler as test_module - - -class TestCredentialIssueHandler(IsolatedAsyncioTestCase): - async def test_called(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_store_credential"] = False - request_context.connection_record = mock.MagicMock() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_credential = mock.CoroutineMock() - request_context.message = CredentialIssue() - request_context.connection_ready = True - handler = test_module.CredentialIssueHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_credential.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - assert not responder.messages - - async def test_called_auto_store(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_store_credential"] = True - request_context.connection_record = mock.MagicMock() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value = mock.MagicMock( - receive_credential=mock.CoroutineMock(), - store_credential=mock.CoroutineMock(), - send_credential_ack=mock.CoroutineMock( - return_value=( - mock.CoroutineMock(), - mock.CoroutineMock(), - ) - ), - ) - request_context.message = CredentialIssue() - request_context.connection_ready = True - handler = test_module.CredentialIssueHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_credential.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - assert mock_cred_mgr.return_value.send_credential_ack.call_count == 1 - - async def test_called_auto_store_x(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_store_credential"] = True - request_context.connection_record = mock.MagicMock() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value = mock.MagicMock( - receive_credential=mock.CoroutineMock( - return_value=mock.MagicMock(save_error_state=mock.CoroutineMock()) - ), - store_credential=mock.CoroutineMock( - side_effect=test_module.IndyHolderError() - ), - send_credential_ack=mock.CoroutineMock( - return_value=( - mock.CoroutineMock(), - mock.CoroutineMock(), - ) - ), - ) - - request_context.message = CredentialIssue() - request_context.connection_ready = True - handler = test_module.CredentialIssueHandler() - responder = MockResponder() - - with mock.patch.object( - responder, "send_reply", mock.CoroutineMock() - ) as mock_send_reply, mock.patch.object( - handler._logger, "exception", mock.MagicMock() - ) as mock_log_exc: - await handler.handle(request_context, responder) - mock_log_exc.assert_called_once() - - async def test_called_not_ready(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_credential = mock.CoroutineMock() - request_context.message = CredentialIssue() - request_context.connection_ready = False - handler = test_module.CredentialIssueHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert err.exception.message == "Connection used for credential not ready" - - assert not responder.messages - - async def test_called_no_connection_no_oob(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - # No oob record found - return_value=None - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_credential = mock.CoroutineMock() - request_context.message = CredentialIssue() - handler = test_module.CredentialIssueHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "No connection or associated connectionless exchange found for credential" - ) - - assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_offer_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_offer_handler.py deleted file mode 100644 index 9809124d14..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_offer_handler.py +++ /dev/null @@ -1,171 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......core.oob_processor import OobMessageProcessor -from ......messaging.request_context import RequestContext -from ......messaging.responder import MockResponder -from ......transport.inbound.receipt import MessageReceipt -from ...messages.credential_offer import CredentialOffer -from .. import credential_offer_handler as test_module - - -class TestCredentialOfferHandler(IsolatedAsyncioTestCase): - async def test_called(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_respond_credential_offer"] = False - request_context.connection_record = mock.MagicMock() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_offer = mock.CoroutineMock() - request_context.message = CredentialOffer() - request_context.connection_ready = True - handler = test_module.CredentialOfferHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_offer.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - assert not responder.messages - - async def test_called_auto_request(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_respond_credential_offer"] = True - request_context.connection_record = mock.MagicMock() - request_context.connection_record.my_did = "dummy" - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_offer = mock.CoroutineMock() - mock_cred_mgr.return_value.create_request = mock.CoroutineMock( - return_value=(None, "credential_request_message") - ) - request_context.message = CredentialOffer() - request_context.connection_ready = True - handler = test_module.CredentialOfferHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_offer.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - messages = responder.messages - assert len(messages) == 1 - (result, target) = messages[0] - assert result == "credential_request_message" - assert target == {} - - async def test_called_auto_request_x(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_respond_credential_offer"] = True - request_context.connection_record = mock.MagicMock() - request_context.connection_record.my_did = "dummy" - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_offer = mock.CoroutineMock( - return_value=mock.MagicMock(save_error_state=mock.CoroutineMock()) - ) - mock_cred_mgr.return_value.create_request = mock.CoroutineMock( - side_effect=test_module.IndyHolderError() - ) - - request_context.message = CredentialOffer() - request_context.connection_ready = True - handler = test_module.CredentialOfferHandler() - responder = MockResponder() - - with mock.patch.object( - responder, "send_reply", mock.CoroutineMock() - ) as mock_send_reply, mock.patch.object( - handler._logger, "exception", mock.MagicMock() - ) as mock_log_exc: - await handler.handle(request_context, responder) - mock_log_exc.assert_called_once() - - async def test_called_not_ready(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_offer = mock.CoroutineMock() - request_context.message = CredentialOffer() - request_context.connection_ready = False - handler = test_module.CredentialOfferHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message == "Connection used for credential offer not ready" - ) - - assert not responder.messages - - async def test_no_conn_no_oob(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - # No oob record found - return_value=None - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_offer = mock.CoroutineMock() - request_context.message = CredentialOffer() - request_context.connection_ready = False - handler = test_module.CredentialOfferHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "No connection or associated connectionless exchange found for credential offer" - ) - - assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_problem_report_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_problem_report_handler.py deleted file mode 100644 index a8837bd01c..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_problem_report_handler.py +++ /dev/null @@ -1,113 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......messaging.request_context import RequestContext -from ......messaging.responder import MockResponder -from ......transport.inbound.receipt import MessageReceipt -from ...messages.credential_problem_report import ( - CredentialProblemReport, - ProblemReportReason, -) -from .. import credential_problem_report_handler as test_module - - -class TestCredentialProblemReportHandler(IsolatedAsyncioTestCase): - async def test_called(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - request_context.connection_ready = True - mock_cred_mgr.return_value.receive_problem_report = mock.CoroutineMock() - request_context.message = CredentialProblemReport( - description={ - "en": "Change of plans", - "code": ProblemReportReason.ISSUANCE_ABANDONED.value, - } - ) - handler = test_module.CredentialProblemReportHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_problem_report.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id - ) - assert not responder.messages - - async def test_called_x(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - request_context.connection_ready = True - mock_cred_mgr.return_value.receive_problem_report = mock.CoroutineMock( - side_effect=test_module.StorageError("Disk full") - ) - request_context.message = CredentialProblemReport( - description={ - "en": "Change of plans", - "code": ProblemReportReason.ISSUANCE_ABANDONED.value, - } - ) - handler = test_module.CredentialProblemReportHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_problem_report.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id - ) - assert not responder.messages - - async def test_called_not_ready(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - request_context.connection_ready = False - - request_context.message = CredentialProblemReport( - description={ - "en": "Change of plans", - "code": ProblemReportReason.ISSUANCE_ABANDONED.value, - } - ) - handler = test_module.CredentialProblemReportHandler() - responder = MockResponder() - - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "Connection used for credential problem report not ready" - ) - - async def test_called_no_connection(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = None - - request_context.message = CredentialProblemReport( - description={ - "en": "Change of plans", - "code": ProblemReportReason.ISSUANCE_ABANDONED.value, - } - ) - handler = test_module.CredentialProblemReportHandler() - responder = MockResponder() - - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "Connectionless not supported for credential problem report" - ) - - assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_proposal_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_proposal_handler.py deleted file mode 100644 index 37cd074fe7..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_proposal_handler.py +++ /dev/null @@ -1,133 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......messaging.request_context import RequestContext -from ......messaging.responder import MockResponder -from ......transport.inbound.receipt import MessageReceipt -from ...messages.credential_proposal import CredentialProposal -from .. import credential_proposal_handler as test_module - - -class TestCredentialProposalHandler(IsolatedAsyncioTestCase): - async def test_called(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_proposal = mock.CoroutineMock( - return_value=mock.MagicMock() - ) - mock_cred_mgr.return_value.receive_proposal.return_value.auto_offer = False - request_context.message = CredentialProposal() - request_context.connection_ready = True - handler = test_module.CredentialProposalHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_proposal.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id - ) - assert not responder.messages - - async def test_called_auto_offer(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_proposal = mock.CoroutineMock( - return_value=mock.MagicMock() - ) - mock_cred_mgr.return_value.receive_proposal.return_value.auto_offer = True - mock_cred_mgr.return_value.create_offer = mock.CoroutineMock( - return_value=(None, "credential_offer_message") - ) - request_context.message = CredentialProposal() - request_context.connection_ready = True - handler = test_module.CredentialProposalHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_proposal.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id - ) - messages = responder.messages - assert len(messages) == 1 - (result, target) = messages[0] - assert result == "credential_offer_message" - assert target == {} - - async def test_called_auto_offer_x(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_proposal = mock.CoroutineMock( - return_value=mock.MagicMock(save_error_state=mock.CoroutineMock()) - ) - mock_cred_mgr.return_value.receive_proposal.return_value.auto_offer = True - mock_cred_mgr.return_value.create_offer = mock.CoroutineMock( - side_effect=test_module.IndyIssuerError() - ) - - request_context.message = CredentialProposal() - request_context.connection_ready = True - handler = test_module.CredentialProposalHandler() - responder = MockResponder() - - with mock.patch.object( - responder, "send_reply", mock.CoroutineMock() - ) as mock_send_reply, mock.patch.object( - handler._logger, "exception", mock.MagicMock() - ) as mock_log_exc: - await handler.handle(request_context, responder) - mock_log_exc.assert_called_once() - - async def test_called_not_ready(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_proposal = mock.CoroutineMock() - request_context.message = CredentialProposal() - request_context.connection_ready = False - handler = test_module.CredentialProposalHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "Connection used for credential proposal not ready" - ) - - assert not responder.messages - - async def test_called_no_connection(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - - request_context.message = CredentialProposal() - handler = test_module.CredentialProposalHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "Connectionless not supported for credential proposal" - ) - - assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_request_handler.py b/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_request_handler.py deleted file mode 100644 index 3c8e54cece..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/handlers/tests/test_credential_request_handler.py +++ /dev/null @@ -1,249 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......core.oob_processor import OobMessageProcessor -from ......messaging.request_context import RequestContext -from ......messaging.responder import MockResponder -from ......transport.inbound.receipt import MessageReceipt -from ...messages.credential_request import CredentialRequest -from ...messages.inner.credential_preview import CredAttrSpec, CredentialPreview -from ...models.credential_exchange import V10CredentialExchange -from .. import credential_request_handler as test_module - -CD_ID = "LjgpST2rjsoxYegQDRm7EL:3:CL:18:tag" - - -class TestCredentialRequestHandler(IsolatedAsyncioTestCase): - async def test_called(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - oob_record = mock.MagicMock() - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=oob_record - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=mock.MagicMock() - ) - mock_cred_mgr.return_value.receive_request.return_value.auto_issue = False - request_context.message = CredentialRequest() - request_context.connection_ready = True - handler = test_module.CredentialRequestHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_request.assert_called_once_with( - request_context.message, request_context.connection_record, oob_record - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - assert not responder.messages - - async def test_called_auto_issue(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - oob_record = mock.MagicMock() - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=oob_record - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - ATTR_DICT = {"test": "123", "hello": "world"} - cred_ex_rec = V10CredentialExchange( - credential_proposal_dict={ - "credential_proposal": CredentialPreview( - attributes=(CredAttrSpec.list_plain(ATTR_DICT)) - ).serialize(), - "cred_def_id": CD_ID, - }, - ) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=cred_ex_rec - ) - mock_cred_mgr.return_value.receive_request.return_value.auto_issue = True - mock_cred_mgr.return_value.issue_credential = mock.CoroutineMock( - return_value=(None, "credential_issue_message") - ) - request_context.message = CredentialRequest() - request_context.connection_ready = True - handler = test_module.CredentialRequestHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - mock_cred_mgr.return_value.issue_credential.assert_called_once_with( - cred_ex_record=cred_ex_rec, comment=None - ) - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_request.assert_called_once_with( - request_context.message, request_context.connection_record, oob_record - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - messages = responder.messages - assert len(messages) == 1 - (result, target) = messages[0] - assert result == "credential_issue_message" - assert target == {} - - async def test_called_auto_issue_x(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - oob_record = mock.MagicMock() - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=oob_record - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - ATTR_DICT = {"test": "123", "hello": "world"} - cred_ex_rec = V10CredentialExchange( - credential_proposal_dict={ - "credential_proposal": CredentialPreview( - attributes=(CredAttrSpec.list_plain(ATTR_DICT)) - ).serialize(), - "cred_def_id": CD_ID, - }, - ) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr, mock.patch.object( - cred_ex_rec, "save_error_state", mock.CoroutineMock() - ): - mock_cred_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=cred_ex_rec - ) - mock_cred_mgr.return_value.receive_request.return_value.auto_issue = True - mock_cred_mgr.return_value.issue_credential = mock.CoroutineMock( - side_effect=test_module.IndyIssuerError() - ) - - request_context.message = CredentialRequest() - request_context.connection_ready = True - handler = test_module.CredentialRequestHandler() - responder = MockResponder() - - with mock.patch.object( - responder, "send_reply", mock.CoroutineMock() - ) as mock_send_reply, mock.patch.object( - handler._logger, "exception", mock.MagicMock() - ) as mock_log_exc: - await handler.handle(request_context, responder) - mock_log_exc.assert_called_once() - - async def test_called_auto_issue_no_preview(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - oob_record = mock.MagicMock() - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=oob_record - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - cred_ex_rec = V10CredentialExchange( - credential_proposal_dict={"cred_def_id": CD_ID} - ) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=cred_ex_rec - ) - mock_cred_mgr.return_value.receive_request.return_value.auto_issue = True - mock_cred_mgr.return_value.issue_credential = mock.CoroutineMock( - return_value=(None, "credential_issue_message") - ) - - request_context.message = CredentialRequest() - request_context.connection_ready = True - handler = test_module.CredentialRequestHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - mock_cred_mgr.return_value.issue_credential.assert_not_called() - - mock_cred_mgr.assert_called_once_with(request_context.profile) - mock_cred_mgr.return_value.receive_request.assert_called_once_with( - request_context.message, request_context.connection_record, oob_record - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - assert not responder.messages - - async def test_called_not_ready(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_request = mock.CoroutineMock() - request_context.message = CredentialRequest() - request_context.connection_ready = False - handler = test_module.CredentialRequestHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "Connection used for credential request not ready" - ) - - assert not responder.messages - - async def test_called_no_connection_no_oob(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - # No oob record found - return_value=None - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr: - mock_cred_mgr.return_value.receive_request = mock.CoroutineMock() - request_context.message = CredentialRequest() - handler = test_module.CredentialRequestHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "No connection or associated connectionless exchange found for credential request" - ) - - assert not responder.messages diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/manager.py deleted file mode 100644 index 9cbd5b6aec..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/manager.py +++ /dev/null @@ -1,1006 +0,0 @@ -"""Classes to manage credentials.""" - -import asyncio -import json -import logging -from typing import Mapping, Optional, Tuple - -from ....cache.base import BaseCache -from ....connections.models.conn_record import ConnRecord -from ....core.error import BaseError -from ....core.profile import Profile -from ....indy.holder import IndyHolder, IndyHolderError -from ....indy.issuer import IndyIssuer, IndyIssuerRevocationRegistryFullError -from ....ledger.multiple_ledger.ledger_requests_executor import ( - GET_CRED_DEF, - GET_SCHEMA, - IndyLedgerRequestsExecutor, -) -from ....messaging.credential_definitions.util import ( - CRED_DEF_SENT_RECORD_TYPE, - CRED_DEF_TAGS, -) -from ....messaging.responder import BaseResponder -from ....multitenant.base import BaseMultitenantManager -from ....revocation.indy import IndyRevocation -from ....revocation.models.issuer_cred_rev_record import IssuerCredRevRecord -from ....revocation.models.revocation_registry import RevocationRegistry -from ....storage.base import BaseStorage -from ....storage.error import StorageError, StorageNotFoundError -from ...out_of_band.v1_0.models.oob_record import OobRecord -from .messages.credential_ack import CredentialAck -from .messages.credential_issue import CredentialIssue -from .messages.credential_offer import CredentialOffer -from .messages.credential_problem_report import ( - CredentialProblemReport, - ProblemReportReason, -) -from .messages.credential_proposal import CredentialProposal -from .messages.credential_request import CredentialRequest -from .messages.inner.credential_preview import CredentialPreview -from .models.credential_exchange import V10CredentialExchange - -LOGGER = logging.getLogger(__name__) - - -class CredentialManagerError(BaseError): - """Credential error.""" - - -class CredentialManager: - """Class for managing credentials.""" - - def __init__(self, profile: Profile): - """Initialize a CredentialManager. - - Args: - profile: The profile instance for this credential manager - """ - self._profile = profile - - @property - def profile(self) -> Profile: - """Accessor for the current profile instance. - - Returns: - The profile instance for this credential manager - - """ - return self._profile - - async def _match_sent_cred_def_id(self, tag_query: Mapping[str, str]) -> str: - """Return most recent matching id of cred def that agent sent to ledger.""" - - async with self._profile.session() as session: - storage = session.inject(BaseStorage) - found = await storage.find_all_records( - type_filter=CRED_DEF_SENT_RECORD_TYPE, tag_query=tag_query - ) - if not found: - raise CredentialManagerError( - f"Issuer has no operable cred def for proposal spec {tag_query}" - ) - return max(found, key=lambda r: int(r.tags["epoch"])).tags["cred_def_id"] - - async def prepare_send( - self, - connection_id: str, - credential_proposal: CredentialProposal, - auto_remove: Optional[bool] = None, - comment: Optional[str] = None, - ) -> Tuple[V10CredentialExchange, CredentialOffer]: - """Set up a new credential exchange for an automated send. - - Args: - connection_id: Connection to create offer for - credential_proposal: The credential proposal with preview - auto_remove: Flag to automatically remove the record on completion - comment: Optional human-readable comment to set in offer message - - Returns: - A tuple of the new credential exchange record and credential offer message - - """ - if auto_remove is None: - auto_remove = not self._profile.settings.get("preserve_exchange_records") - credential_exchange = V10CredentialExchange( - connection_id=connection_id, - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - credential_proposal_dict=credential_proposal, - auto_issue=True, - auto_remove=auto_remove, - trace=(credential_proposal._trace is not None), - ) - (credential_exchange, credential_offer) = await self.create_offer( - cred_ex_record=credential_exchange, - counter_proposal=None, - comment=comment, - ) - return (credential_exchange, credential_offer) - - async def create_proposal( - self, - connection_id: str, - *, - auto_offer: Optional[bool] = None, - auto_remove: Optional[bool] = None, - comment: Optional[str] = None, - credential_preview: Optional[CredentialPreview] = None, - schema_id: Optional[str] = None, - schema_issuer_did: Optional[str] = None, - schema_name: Optional[str] = None, - schema_version: Optional[str] = None, - cred_def_id: Optional[str] = None, - issuer_did: Optional[str] = None, - trace: bool = False, - ) -> V10CredentialExchange: - """Create a credential proposal. - - Args: - connection_id: Connection to create proposal for - auto_offer: Should this proposal request automatically be handled to - offer a credential - auto_remove: Should the record be automatically removed on completion - comment: Optional human-readable comment to include in proposal - credential_preview: The credential preview to use to create - the credential proposal - schema_id: Schema id for credential proposal - schema_issuer_did: Schema issuer DID for credential proposal - schema_name: Schema name for credential proposal - schema_version: Schema version for credential proposal - cred_def_id: Credential definition id for credential proposal - issuer_did: Issuer DID for credential proposal - trace: Whether to trace the operation - - Returns: - Resulting credential exchange record including credential proposal - - """ - credential_proposal_message = CredentialProposal( - comment=comment, - credential_proposal=credential_preview, - schema_id=schema_id, - schema_issuer_did=schema_issuer_did, - schema_name=schema_name, - schema_version=schema_version, - cred_def_id=cred_def_id, - issuer_did=issuer_did, - ) - credential_proposal_message.assign_trace_decorator(self._profile.settings, trace) - - if auto_remove is None: - auto_remove = not self._profile.settings.get("preserve_exchange_records") - cred_ex_record = V10CredentialExchange( - connection_id=connection_id, - thread_id=credential_proposal_message._thread_id, - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_HOLDER, - state=V10CredentialExchange.STATE_PROPOSAL_SENT, - credential_proposal_dict=credential_proposal_message, - auto_offer=auto_offer, - auto_remove=auto_remove, - trace=trace, - ) - async with self._profile.session() as session: - await cred_ex_record.save(session, reason="create credential proposal") - return cred_ex_record - - async def receive_proposal( - self, message: CredentialProposal, connection_id: str - ) -> V10CredentialExchange: - """Receive a credential proposal. - - Returns: - The resulting credential exchange record, created - - """ - # at this point, cred def and schema still open to potential negotiation - cred_ex_record = V10CredentialExchange( - connection_id=connection_id, - thread_id=message._thread_id, - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_PROPOSAL_RECEIVED, - credential_proposal_dict=message, - auto_offer=self._profile.settings.get( - "debug.auto_respond_credential_proposal" - ), - auto_issue=self._profile.settings.get( - "debug.auto_respond_credential_request" - ), - auto_remove=not self._profile.settings.get("preserve_exchange_records"), - trace=(message._trace is not None), - ) - async with self._profile.session() as session: - await cred_ex_record.save(session, reason="receive credential proposal") - - return cred_ex_record - - async def create_offer( - self, - cred_ex_record: V10CredentialExchange, - counter_proposal: Optional[CredentialProposal] = None, - comment: Optional[str] = None, - ) -> Tuple[V10CredentialExchange, CredentialOffer]: - """Create a credential offer, update credential exchange record. - - Args: - cred_ex_record: Credential exchange to create offer for - counter_proposal: optional proposal to counter - comment: optional human-readable comment to set in offer message - - Returns: - A tuple (credential exchange record, credential offer message) - - """ - - async def _create(cred_def_id): - issuer = self._profile.inject(IndyIssuer) - offer_json = await issuer.create_credential_offer(cred_def_id) - return json.loads(offer_json) - - credential_proposal_message = ( - counter_proposal - if counter_proposal - else cred_ex_record.credential_proposal_dict - ) - credential_proposal_message.assign_trace_decorator( - self._profile.settings, cred_ex_record.trace - ) - cred_def_id = await self._match_sent_cred_def_id( - { - t: getattr(credential_proposal_message, t) - for t in CRED_DEF_TAGS - if getattr(credential_proposal_message, t) - } - ) - - credential_preview = credential_proposal_message.credential_proposal - - # vet attributes - multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) - if multitenant_mgr: - ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) - else: - ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) - ledger = ( - await ledger_exec_inst.get_ledger_for_identifier( - cred_def_id, - txn_record_type=GET_CRED_DEF, - ) - )[1] - async with ledger: - schema_id = await ledger.credential_definition_id2schema_id(cred_def_id) - schema = await ledger.get_schema(schema_id) - schema_attrs = set(schema["attrNames"]) - preview_attrs = set(credential_preview.attr_dict()) - if preview_attrs != schema_attrs: - raise CredentialManagerError( - f"Preview attributes {preview_attrs} " - f"mismatch corresponding schema attributes {schema_attrs}" - ) - - credential_offer = None - cache_key = f"credential_offer::{cred_def_id}" - cache = self._profile.inject_or(BaseCache) - if cache: - async with cache.acquire(cache_key) as entry: - if entry.result: - credential_offer = entry.result - else: - credential_offer = await _create(cred_def_id) - await entry.set_result(credential_offer, 3600) - if not credential_offer: - credential_offer = await _create(cred_def_id) - - credential_offer_message = CredentialOffer( - comment=comment, - credential_preview=credential_preview, - offers_attach=[CredentialOffer.wrap_indy_offer(credential_offer)], - ) - - credential_offer_message._thread = {"thid": credential_offer_message._thread_id} - credential_offer_message.assign_trace_decorator( - self._profile.settings, cred_ex_record.trace - ) - - cred_ex_record.thread_id = credential_offer_message._thread_id - cred_ex_record.schema_id = credential_offer["schema_id"] - cred_ex_record.credential_definition_id = credential_offer["cred_def_id"] - cred_ex_record.state = V10CredentialExchange.STATE_OFFER_SENT - cred_ex_record.credential_proposal_dict = ( # any counter replaces original - credential_proposal_message - ) - cred_ex_record.credential_offer = credential_offer - - cred_ex_record.credential_offer_dict = credential_offer_message - - async with self._profile.session() as session: - await cred_ex_record.save(session, reason="create credential offer") - - return (cred_ex_record, credential_offer_message) - - async def receive_offer( - self, message: CredentialOffer, connection_id: Optional[str] - ) -> V10CredentialExchange: - """Receive a credential offer. - - Returns: - The credential exchange record, updated - - """ - credential_preview = message.credential_preview - indy_offer = message.indy_offer(0) - schema_id = indy_offer["schema_id"] - cred_def_id = indy_offer["cred_def_id"] - - credential_proposal_dict = CredentialProposal( - comment=message.comment, - credential_proposal=credential_preview, - schema_id=schema_id, - cred_def_id=cred_def_id, - ) - - async with self._profile.transaction() as txn: - # Get credential exchange record (holder sent proposal first) - # or create it (issuer sent offer first) - try: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - txn, - connection_id, - message._thread_id, - role=V10CredentialExchange.ROLE_HOLDER, - for_update=True, - ) - ) - except StorageNotFoundError: # issuer sent this offer free of any proposal - cred_ex_record = V10CredentialExchange( - connection_id=connection_id, - thread_id=message._thread_id, - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_HOLDER, - auto_remove=not self._profile.settings.get( - "preserve_exchange_records" - ), - trace=(message._trace is not None), - ) - else: - if cred_ex_record.state != V10CredentialExchange.STATE_PROPOSAL_SENT: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_PROPOSAL_SENT})" - ) - - cred_ex_record.credential_proposal_dict = credential_proposal_dict - cred_ex_record.credential_offer_dict = message - cred_ex_record.credential_offer = indy_offer - cred_ex_record.state = V10CredentialExchange.STATE_OFFER_RECEIVED - cred_ex_record.schema_id = schema_id - cred_ex_record.credential_definition_id = cred_def_id - - await cred_ex_record.save(txn, reason="receive credential offer") - await txn.commit() - - return cred_ex_record - - async def create_request( - self, cred_ex_record: V10CredentialExchange, holder_did: str - ) -> Tuple[V10CredentialExchange, CredentialRequest]: - """Create a credential request. - - Args: - cred_ex_record: Credential exchange record - for which to create request - holder_did: holder DID - - Returns: - A tuple (credential exchange record, credential request message) - - """ - credential_definition_id = cred_ex_record.credential_definition_id - cred_offer_ser = cred_ex_record._credential_offer.ser - cred_req_ser = None - cred_req_meta = None - - # hold on to values that may have changed so we can restore after fetch - auto_remove = cred_ex_record.auto_remove - - async def _create(): - multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) - if multitenant_mgr: - ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) - else: - ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) - ledger = ( - await ledger_exec_inst.get_ledger_for_identifier( - credential_definition_id, - txn_record_type=GET_CRED_DEF, - ) - )[1] - async with ledger: - credential_definition = await ledger.get_credential_definition( - credential_definition_id - ) - - holder = self._profile.inject(IndyHolder) - request_json, metadata_json = await holder.create_credential_request( - cred_offer_ser, - credential_definition, - holder_did, - ) - return { - "request": json.loads(request_json), - "metadata": json.loads(metadata_json), - } - - if cred_ex_record.state == V10CredentialExchange.STATE_REQUEST_SENT: - LOGGER.warning( - "create_request called multiple times for v1.0 credential exchange: %s", - cred_ex_record.credential_exchange_id, - ) - cred_req_ser = cred_ex_record._credential_request.ser - cred_req_meta = cred_ex_record.credential_request_metadata - elif cred_ex_record.state == V10CredentialExchange.STATE_OFFER_RECEIVED: - nonce = cred_offer_ser["nonce"] - cache_key = ( - f"credential_request::{credential_definition_id}::{holder_did}::{nonce}" - ) - cred_req_result = None - cache = self._profile.inject_or(BaseCache) - if cache: - async with cache.acquire(cache_key) as entry: - if entry.result: - cred_req_result = entry.result - else: - cred_req_result = await _create() - await entry.set_result(cred_req_result, 3600) - if not cred_req_result: - cred_req_result = await _create() - cred_req_ser = cred_req_result["request"] - cred_req_meta = cred_req_result["metadata"] - - async with self._profile.transaction() as txn: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - txn, cred_ex_record.credential_exchange_id, for_update=True - ) - if cred_ex_record.state != V10CredentialExchange.STATE_OFFER_RECEIVED: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_OFFER_RECEIVED})" - ) - - cred_ex_record.credential_request = cred_req_ser - cred_ex_record.credential_request_metadata = cred_req_meta - cred_ex_record.state = V10CredentialExchange.STATE_REQUEST_SENT - # restore values passed in... - cred_ex_record.auto_remove = auto_remove - await cred_ex_record.save(txn, reason="create credential request") - await txn.commit() - else: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_OFFER_RECEIVED})" - ) - - credential_request_message = CredentialRequest( - requests_attach=[CredentialRequest.wrap_indy_cred_req(cred_req_ser)] - ) - # Assign thid (and optionally pthid) to message - credential_request_message.assign_thread_from( - cred_ex_record.credential_offer_dict - ) - credential_request_message.assign_trace_decorator( - self._profile.settings, cred_ex_record.trace - ) - - return (cred_ex_record, credential_request_message) - - async def receive_request( - self, - message: CredentialRequest, - connection_record: Optional[ConnRecord], - oob_record: Optional[OobRecord], - ): - """Receive a credential request. - - Args: - message (CredentialRequest): The credential request message to receive. - connection_record (Optional[ConnRecord]): The connection record associated - with the request. - oob_record (Optional[OobRecord]): The out-of-band record associated with the - request. - - Returns: - V10CredentialExchange: The credential exchange record, retrieved and updated. - - Raises: - StorageNotFoundError: If the credential exchange record is not found. - - """ - assert len(message.requests_attach or []) == 1 - credential_request = message.indy_cred_req(0) - - # connection_id is None in the record if this is in response to - # an request~attach from an OOB message. If so, we do not want to filter - # the record by connection_id. - connection_id = None if oob_record else connection_record.connection_id - - async with self._profile.transaction() as txn: - try: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - txn, - connection_id, - message._thread_id, - role=V10CredentialExchange.ROLE_ISSUER, - for_update=True, - ) - ) - except StorageNotFoundError as ex: - LOGGER.error( - f"Credential Exchange (thread id = {message._thread_id}) not found." - " Indy issue credential format can't start from credential request.", - ) - raise ex - if cred_ex_record.state != V10CredentialExchange.STATE_OFFER_SENT: - LOGGER.error( - "Skipping credential request; exchange state is %s (id=%s)", - cred_ex_record.state, - cred_ex_record.credential_exchange_id, - ) - return None - - if connection_record: - cred_ex_record.connection_id = connection_record.connection_id - - cred_ex_record.credential_request = credential_request - cred_ex_record.state = V10CredentialExchange.STATE_REQUEST_RECEIVED - await cred_ex_record.save(txn, reason="receive credential request") - await txn.commit() - - return cred_ex_record - - async def issue_credential( - self, - cred_ex_record: V10CredentialExchange, - *, - comment: Optional[str] = None, - retries: int = 5, - ) -> Tuple[V10CredentialExchange, CredentialIssue]: - """Issue a credential. - - Args: - cred_ex_record: The credential exchange record - for which to issue a credential - comment: optional human-readable comment pertaining to credential issue - retries: how many times to retry on error - - Returns: - Tuple: (Updated credential exchange record, credential message) - - """ - - credential_ser = None - - if cred_ex_record.credential: - LOGGER.warning( - "issue_credential called multiple times for v1.0 credential exchange %s", - cred_ex_record.credential_exchange_id, - ) - credential_ser = cred_ex_record._credential.ser - - elif cred_ex_record.state != V10CredentialExchange.STATE_REQUEST_RECEIVED: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_REQUEST_RECEIVED})" - ) - - else: - cred_offer_ser = cred_ex_record._credential_offer.ser - cred_req_ser = cred_ex_record._credential_request.ser - cred_values = ( - cred_ex_record.credential_proposal_dict.credential_proposal.attr_dict( - decode=False - ) - ) - schema_id = cred_ex_record.schema_id - cred_def_id = cred_ex_record.credential_definition_id - - issuer = self.profile.inject(IndyIssuer) - multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) - if multitenant_mgr: - ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) - else: - ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) - ledger = ( - await ledger_exec_inst.get_ledger_for_identifier( - schema_id, - txn_record_type=GET_SCHEMA, - ) - )[1] - async with ledger: - schema = await ledger.get_schema(schema_id) - credential_definition = await ledger.get_credential_definition( - cred_ex_record.credential_definition_id - ) - revocable = credential_definition["value"].get("revocation") - - for attempt in range(max(retries, 1)): - if attempt > 0: - LOGGER.info( - "Waiting 2s before retrying credential issuance " - "for cred def '%s'", - cred_def_id, - ) - await asyncio.sleep(2) - - if revocable: - revoc = IndyRevocation(self._profile) - registry_info = await revoc.get_or_create_active_registry(cred_def_id) - if not registry_info: - continue - del revoc - issuer_rev_reg, rev_reg = registry_info - rev_reg_id = issuer_rev_reg.revoc_reg_id - tails_path = rev_reg.tails_local_path - else: - rev_reg_id = None - tails_path = None - - try: - (credential_json, cred_rev_id) = await issuer.create_credential( - schema, - cred_offer_ser, - cred_req_ser, - cred_values, - rev_reg_id, - tails_path, - ) - except IndyIssuerRevocationRegistryFullError: - # unlucky, another instance filled the registry first - continue - - if revocable and rev_reg.max_creds <= int(cred_rev_id): - revoc = IndyRevocation(self._profile) - await revoc.handle_full_registry(rev_reg_id) - del revoc - - credential_ser = json.loads(credential_json) - break - - if not credential_ser: - raise CredentialManagerError( - f"Cred def id {cred_ex_record.credential_definition_id} " - "has no active revocation registry" - ) from None - - async with self._profile.transaction() as txn: - if revocable and cred_rev_id: - issuer_cr_rec = IssuerCredRevRecord( - state=IssuerCredRevRecord.STATE_ISSUED, - cred_ex_id=cred_ex_record.credential_exchange_id, - cred_ex_version=IssuerCredRevRecord.VERSION_1, - rev_reg_id=rev_reg_id, - cred_rev_id=cred_rev_id, - ) - await issuer_cr_rec.save( - txn, - reason=( - "Created issuer cred rev record for " - f"rev reg id {rev_reg_id}, index {cred_rev_id}" - ), - ) - - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - txn, cred_ex_record.credential_exchange_id, for_update=True - ) - if cred_ex_record.state != V10CredentialExchange.STATE_REQUEST_RECEIVED: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_REQUEST_RECEIVED})" - ) - cred_ex_record.state = V10CredentialExchange.STATE_ISSUED - cred_ex_record.credential = credential_ser - cred_ex_record.revoc_reg_id = rev_reg_id - cred_ex_record.revocation_id = cred_rev_id - await cred_ex_record.save(txn, reason="issue credential") - await txn.commit() - - credential_message = CredentialIssue( - comment=comment, - credentials_attach=[CredentialIssue.wrap_indy_credential(credential_ser)], - ) - credential_message._thread = {"thid": cred_ex_record.thread_id} - credential_message.assign_trace_decorator( - self._profile.settings, cred_ex_record.trace - ) - - return (cred_ex_record, credential_message) - - async def receive_credential( - self, message: CredentialIssue, connection_id: Optional[str] - ) -> V10CredentialExchange: - """Receive a credential from an issuer. - - Hold in storage potentially to be processed by controller before storing. - - Returns: - Credential exchange record, retrieved and updated - - """ - assert len(message.credentials_attach or []) == 1 - raw_credential = message.indy_credential(0) - - async with self._profile.transaction() as txn: - try: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - txn, - connection_id, - message._thread_id, - role=V10CredentialExchange.ROLE_HOLDER, - for_update=True, - ) - ) - except StorageNotFoundError: - raise CredentialManagerError( - "No credential exchange record found for received credential" - ) from None - if cred_ex_record.state != V10CredentialExchange.STATE_REQUEST_SENT: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_REQUEST_SENT})" - ) - cred_ex_record.raw_credential = raw_credential - cred_ex_record.state = V10CredentialExchange.STATE_CREDENTIAL_RECEIVED - - await cred_ex_record.save(txn, reason="receive credential") - await txn.commit() - - return cred_ex_record - - async def store_credential( - self, cred_ex_record: V10CredentialExchange, credential_id: Optional[str] = None - ) -> V10CredentialExchange: - """Store a credential in holder wallet; send ack to issuer. - - Args: - cred_ex_record: credential exchange record - with credential to store and ack - credential_id: optional credential identifier to override default on storage - - Returns: - Updated credential exchange record - - """ - if cred_ex_record.state != V10CredentialExchange.STATE_CREDENTIAL_RECEIVED: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_CREDENTIAL_RECEIVED})" - ) - - raw_cred_serde = cred_ex_record._raw_credential - revoc_reg_def = None - multitenant_mgr = self.profile.inject_or(BaseMultitenantManager) - if multitenant_mgr: - ledger_exec_inst = IndyLedgerRequestsExecutor(self.profile) - else: - ledger_exec_inst = self.profile.inject(IndyLedgerRequestsExecutor) - ledger = ( - await ledger_exec_inst.get_ledger_for_identifier( - raw_cred_serde.de.cred_def_id, - txn_record_type=GET_CRED_DEF, - ) - )[1] - async with ledger: - credential_definition = await ledger.get_credential_definition( - raw_cred_serde.de.cred_def_id - ) - if raw_cred_serde.de.rev_reg_id: - revoc_reg_def = await ledger.get_revoc_reg_def( - raw_cred_serde.de.rev_reg_id - ) - - holder = self._profile.inject(IndyHolder) - if ( - cred_ex_record.credential_proposal_dict - and cred_ex_record.credential_proposal_dict.credential_proposal - ): - mime_types = ( - cred_ex_record.credential_proposal_dict.credential_proposal.mime_types() - ) - else: - mime_types = None - - if revoc_reg_def: - revoc_reg = RevocationRegistry.from_definition(revoc_reg_def, True) - await revoc_reg.get_or_fetch_local_tails_path() - try: - credential_id = await holder.store_credential( - credential_definition, - raw_cred_serde.ser, - cred_ex_record.credential_request_metadata, - mime_types, - credential_id=credential_id, - rev_reg_def=revoc_reg_def, - ) - except IndyHolderError as e: - LOGGER.error("Error storing credential: %s: %s", e.error_code, e.message) - raise e - - credential_json = await holder.get_credential(credential_id) - credential = json.loads(credential_json) - - async with self._profile.transaction() as txn: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - txn, cred_ex_record.credential_exchange_id, for_update=True - ) - if cred_ex_record.state != V10CredentialExchange.STATE_CREDENTIAL_RECEIVED: - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_CREDENTIAL_RECEIVED})" - ) - - cred_ex_record.credential_id = credential_id - cred_ex_record.credential = credential - cred_ex_record.revoc_reg_id = credential.get("rev_reg_id", None) - cred_ex_record.revocation_id = credential.get("cred_rev_id", None) - await cred_ex_record.save(txn, reason="store credential") - await txn.commit() - - return cred_ex_record - - async def send_credential_ack( - self, - cred_ex_record: V10CredentialExchange, - ) -> Tuple[V10CredentialExchange, CredentialAck]: - """Create, send, and return ack message for input credential exchange record. - - Delete credential exchange record if set to auto-remove. - - Returns: - a tuple of the updated credential exchange record - and the credential ack message for tracing - - """ - credential_ack_message = CredentialAck() - credential_ack_message.assign_thread_id( - cred_ex_record.thread_id, cred_ex_record.parent_thread_id - ) - credential_ack_message.assign_trace_decorator( - self._profile.settings, cred_ex_record.trace - ) - - try: - async with self._profile.transaction() as txn: - try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - txn, cred_ex_record.credential_exchange_id, for_update=True - ) - except StorageNotFoundError: - LOGGER.warning( - "Skipping credential exchange ack, record not found: '%s'", - cred_ex_record.credential_exchange_id, - ) - return (cred_ex_record, None) - - if ( - cred_ex_record.state - != V10CredentialExchange.STATE_CREDENTIAL_RECEIVED - ): - LOGGER.warning( - "Skipping credential exchange ack, state is '%s' for record '%s'", - cred_ex_record.state, - cred_ex_record.credential_exchange_id, - ) - return (cred_ex_record, None) - - cred_ex_record.state = V10CredentialExchange.STATE_ACKED - await cred_ex_record.save(txn, reason="ack credential") - await txn.commit() - - if cred_ex_record.auto_remove: - async with self._profile.session() as session: - await cred_ex_record.delete_record(session) # all done: delete - - except StorageError: - LOGGER.exception( - "Error updating credential exchange" - ) # holder still owes an ack: carry on - - responder = self._profile.inject_or(BaseResponder) - if responder: - await responder.send_reply( - credential_ack_message, - connection_id=cred_ex_record.connection_id, - ) - else: - LOGGER.warning( - "Configuration has no BaseResponder: cannot ack credential on %s", - cred_ex_record.thread_id, - ) - - return (cred_ex_record, credential_ack_message) - - async def receive_credential_ack( - self, message: CredentialAck, connection_id: Optional[str] - ) -> Optional[V10CredentialExchange]: - """Receive credential ack from holder. - - Returns: - credential exchange record, retrieved and updated - - """ - async with self._profile.transaction() as txn: - try: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - txn, - connection_id, - message._thread_id, - role=V10CredentialExchange.ROLE_ISSUER, - for_update=True, - ) - ) - except StorageNotFoundError: - LOGGER.warning( - "Skip ack message on credential exchange, record not found %s", - message._thread_id, - ) - return None - - if cred_ex_record.state == V10CredentialExchange.STATE_ACKED: - return None - cred_ex_record.state = V10CredentialExchange.STATE_ACKED - await cred_ex_record.save(txn, reason="credential acked") - await txn.commit() - - if cred_ex_record.auto_remove: - async with self._profile.session() as session: - await cred_ex_record.delete_record(session) # all done: delete - - return cred_ex_record - - async def receive_problem_report( - self, message: CredentialProblemReport, connection_id: str - ): - """Receive problem report. - - Returns: - credential exchange record, retrieved and updated - - """ - async with self._profile.transaction() as txn: - try: - cred_ex_record = await ( - V10CredentialExchange.retrieve_by_connection_and_thread( - txn, connection_id, message._thread_id, for_update=True - ) - ) - except StorageNotFoundError: - LOGGER.warning( - "Skip problem report on credential exchange, record not found %s", - message._thread_id, - ) - return None - - cred_ex_record.state = V10CredentialExchange.STATE_ABANDONED - code = message.description.get( - "code", - ProblemReportReason.ISSUANCE_ABANDONED.value, - ) - cred_ex_record.error_msg = f"{code}: {message.description.get('en', code)}" - await cred_ex_record.save(txn, reason="received problem report") - await txn.commit() - - return cred_ex_record diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/message_types.py b/aries_cloudagent/protocols/issue_credential/v1_0/message_types.py deleted file mode 100644 index a0a501bf3d..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/message_types.py +++ /dev/null @@ -1,54 +0,0 @@ -"""Message and inner object type identifiers for Connections.""" - -from ...didcomm_prefix import DIDCommPrefix - -SPEC_URI = ( - "https://github.com/hyperledger/aries-rfcs/tree/" - "bb42a6c35e0d5543718fb36dd099551ab192f7b0/features/0036-issue-credential" -) - -# Message types -CREDENTIAL_PROPOSAL = "issue-credential/1.0/propose-credential" -CREDENTIAL_OFFER = "issue-credential/1.0/offer-credential" -CREDENTIAL_REQUEST = "issue-credential/1.0/request-credential" -CREDENTIAL_ISSUE = "issue-credential/1.0/issue-credential" -CREDENTIAL_ACK = "issue-credential/1.0/ack" -CREDENTIAL_PROBLEM_REPORT = "issue-credential/1.0/problem-report" - -PROTOCOL_PACKAGE = "aries_cloudagent.protocols.issue_credential.v1_0" - -MESSAGE_TYPES = DIDCommPrefix.qualify_all( - { - CREDENTIAL_PROPOSAL: ( - f"{PROTOCOL_PACKAGE}.messages.credential_proposal.CredentialProposal" - ), - CREDENTIAL_OFFER: ( - f"{PROTOCOL_PACKAGE}.messages.credential_offer.CredentialOffer" - ), - CREDENTIAL_REQUEST: ( - f"{PROTOCOL_PACKAGE}.messages.credential_request.CredentialRequest" - ), - CREDENTIAL_ISSUE: ( - f"{PROTOCOL_PACKAGE}.messages.credential_issue.CredentialIssue" - ), - CREDENTIAL_ACK: f"{PROTOCOL_PACKAGE}.messages.credential_ack.CredentialAck", - CREDENTIAL_PROBLEM_REPORT: ( - f"{PROTOCOL_PACKAGE}.messages.credential_problem_report." - "CredentialProblemReport" - ), - } -) - -# Inner object types -CREDENTIAL_PREVIEW = "issue-credential/1.0/credential-preview" - -# Identifiers to use in attachment decorators -ATTACH_DECO_IDS = { - CREDENTIAL_OFFER: "libindy-cred-offer-0", - CREDENTIAL_REQUEST: "libindy-cred-request-0", - CREDENTIAL_ISSUE: "libindy-cred-0", -} - -CONTROLLERS = DIDCommPrefix.qualify_all( - {"issue-credential/1.0": f"{PROTOCOL_PACKAGE}.controller.Controller"} -) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/__init__.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_ack.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_ack.py deleted file mode 100644 index 9f9319214f..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_ack.py +++ /dev/null @@ -1,33 +0,0 @@ -"""A credential ack message.""" - -from marshmallow import EXCLUDE - -from ....notification.v1_0.messages.ack import V10Ack, V10AckSchema -from ..message_types import CREDENTIAL_ACK, PROTOCOL_PACKAGE - -HANDLER_CLASS = f"{PROTOCOL_PACKAGE}.handlers.credential_ack_handler.CredentialAckHandler" - - -class CredentialAck(V10Ack): - """Class representing a credential ack message.""" - - class Meta: - """Credential ack metadata.""" - - handler_class = HANDLER_CLASS - schema_class = "CredentialAckSchema" - message_type = CREDENTIAL_ACK - - def __init__(self, **kwargs): - """Initialize credential ack object.""" - super().__init__(**kwargs) - - -class CredentialAckSchema(V10AckSchema): - """Credential ack schema.""" - - class Meta: - """Schema metadata.""" - - model_class = CredentialAck - unknown = EXCLUDE diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py deleted file mode 100644 index 2f6731f971..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_exchange_webhook.py +++ /dev/null @@ -1,48 +0,0 @@ -"""v1.0 credential exchange webhook.""" - - -class V10CredentialExchangeWebhook: - """Class representing a state only credential exchange webhook.""" - - __acceptable_keys_list = [ - "connection_id", - "credential_exchange_id", - "cred_ex_id", - "cred_def_id", - "role", - "initiator", - "revoc_reg_id", - "revocation_id", - "auto_offer", - "auto_issue", - "auto_remove", - "error_msg", - "thread_id", - "parent_thread_id", - "state", - "credential_definition_id", - "schema_id", - "credential_id", - "trace", - "public_did", - "cred_id_stored", - "conn_id", - "created_at", - "updated_at", - ] - - def __init__( - self, - **kwargs, - ): - """Initialize webhook object from V10CredentialExchange. - - from a list of accepted attributes. - """ - [ - self.__setattr__(key, kwargs.get(key)) - for key in self.__acceptable_keys_list - if kwargs.get(key) is not None - ] - if kwargs.get("_id") is not None: - self.credential_exchange_id = kwargs.get("_id") diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_issue.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_issue.py deleted file mode 100644 index 282ba2e47f..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_issue.py +++ /dev/null @@ -1,83 +0,0 @@ -"""A credential content message.""" - -from typing import Optional, Sequence - -from marshmallow import EXCLUDE, fields - -from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from .....messaging.decorators.attach_decorator import ( - AttachDecorator, - AttachDecoratorSchema, -) -from ..message_types import ATTACH_DECO_IDS, CREDENTIAL_ISSUE, PROTOCOL_PACKAGE - -HANDLER_CLASS = ( - f"{PROTOCOL_PACKAGE}.handlers.credential_issue_handler.CredentialIssueHandler" -) - - -class CredentialIssue(AgentMessage): - """Class representing a credential.""" - - class Meta: - """Credential metadata.""" - - handler_class = HANDLER_CLASS - schema_class = "CredentialIssueSchema" - message_type = CREDENTIAL_ISSUE - - def __init__( - self, - _id: Optional[str] = None, - *, - comment: Optional[str] = None, - credentials_attach: Sequence[AttachDecorator] = None, - **kwargs, - ): - """Initialize credential issue object. - - Args: - comment: optional comment - credentials_attach: credentials attachments - kwargs: additional key-value arguments to map into message class properties - - """ - super().__init__(_id=_id, **kwargs) - self.comment = comment - self.credentials_attach = list(credentials_attach) if credentials_attach else [] - - def indy_credential(self, index: int = 0): - """Retrieve and decode indy credential from attachment. - - Args: - index: ordinal in attachment list to decode and return - (typically, list has length 1) - - """ - return self.credentials_attach[index].content - - @classmethod - def wrap_indy_credential(cls, indy_cred: dict) -> AttachDecorator: - """Convert an indy credential offer to an attachment decorator.""" - return AttachDecorator.data_base64( - mapping=indy_cred, ident=ATTACH_DECO_IDS[CREDENTIAL_ISSUE] - ) - - -class CredentialIssueSchema(AgentMessageSchema): - """Credential schema.""" - - class Meta: - """Credential schema metadata.""" - - model_class = CredentialIssue - unknown = EXCLUDE - - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - credentials_attach = fields.Nested( - AttachDecoratorSchema, required=True, many=True, data_key="credentials~attach" - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_offer.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_offer.py deleted file mode 100644 index 0e032fd063..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_offer.py +++ /dev/null @@ -1,88 +0,0 @@ -"""A credential offer content message.""" - -from typing import Optional, Sequence - -from marshmallow import EXCLUDE, fields - -from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from .....messaging.decorators.attach_decorator import ( - AttachDecorator, - AttachDecoratorSchema, -) -from ..message_types import ATTACH_DECO_IDS, CREDENTIAL_OFFER, PROTOCOL_PACKAGE -from .inner.credential_preview import CredentialPreview, CredentialPreviewSchema - -HANDLER_CLASS = ( - f"{PROTOCOL_PACKAGE}.handlers.credential_offer_handler.CredentialOfferHandler" -) - - -class CredentialOffer(AgentMessage): - """Class representing a credential offer.""" - - class Meta: - """CredentialOffer metadata.""" - - handler_class = HANDLER_CLASS - schema_class = "CredentialOfferSchema" - message_type = CREDENTIAL_OFFER - - def __init__( - self, - _id: Optional[str] = None, - *, - comment: Optional[str] = None, - credential_preview: Optional[CredentialPreview] = None, - offers_attach: Sequence[AttachDecorator] = None, - **kwargs, - ): - """Initialize credential offer object. - - Args: - comment: optional human-readable comment - credential_preview: credential preview - offers_attach: list of offer attachments - kwargs: additional key-value arguments to map into message class properties - - """ - super().__init__(_id=_id, **kwargs) - self.comment = comment - self.credential_preview = credential_preview - self.offers_attach = list(offers_attach) if offers_attach else [] - - def indy_offer(self, index: int = 0) -> dict: - """Retrieve and decode indy offer from attachment. - - Args: - index: ordinal in attachment list to decode and return - (typically, list has length 1) - - """ - return self.offers_attach[index].content - - @classmethod - def wrap_indy_offer(cls, indy_offer: dict) -> AttachDecorator: - """Convert an indy credential offer to an attachment decorator.""" - return AttachDecorator.data_base64( - mapping=indy_offer, ident=ATTACH_DECO_IDS[CREDENTIAL_OFFER] - ) - - -class CredentialOfferSchema(AgentMessageSchema): - """Credential offer schema.""" - - class Meta: - """Credential offer schema metadata.""" - - model_class = CredentialOffer - unknown = EXCLUDE - - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - credential_preview = fields.Nested(CredentialPreviewSchema, required=False) - offers_attach = fields.Nested( - AttachDecoratorSchema, required=True, many=True, data_key="offers~attach" - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_problem_report.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_problem_report.py deleted file mode 100644 index 2c5372997e..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_problem_report.py +++ /dev/null @@ -1,70 +0,0 @@ -"""A problem report message.""" - -import logging -from enum import Enum - -from marshmallow import EXCLUDE, ValidationError, validates_schema - -from ....problem_report.v1_0.message import ProblemReport, ProblemReportSchema -from ..message_types import CREDENTIAL_PROBLEM_REPORT, PROTOCOL_PACKAGE - -HANDLER_CLASS = ( - f"{PROTOCOL_PACKAGE}.handlers.credential_problem_report_handler." - "CredentialProblemReportHandler" -) - -LOGGER = logging.getLogger(__name__) - - -class ProblemReportReason(Enum): - """Supported reason codes.""" - - ISSUANCE_ABANDONED = "issuance-abandoned" - - -class CredentialProblemReport(ProblemReport): - """Class representing a problem report message.""" - - class Meta: - """Problem report metadata.""" - - handler_class = HANDLER_CLASS - schema_class = "CredentialProblemReportSchema" - message_type = CREDENTIAL_PROBLEM_REPORT - - def __init__(self, *args, **kwargs): - """Initialize problem report object.""" - super().__init__(*args, **kwargs) - - -class CredentialProblemReportSchema(ProblemReportSchema): - """Problem report schema.""" - - class Meta: - """Schema metadata.""" - - model_class = CredentialProblemReport - unknown = EXCLUDE - - @validates_schema - def validate_fields(self, data, **kwargs): - """Validate schema fields. - - Args: - data: The data to validate - kwargs: Additional keyword arguments - - """ - if not data.get("description", {}).get("code", ""): - raise ValidationError("Value for description.code must be present") - elif ( - data.get("description", {}).get("code", "") - != ProblemReportReason.ISSUANCE_ABANDONED.value - ): - locales = list(data.get("description").keys()) - locales.remove("code") - LOGGER.warning( - "Unexpected error code received.\n" - f"Code: {data.get('description').get('code')}, " - f"Description: {data.get('description').get(locales[0])}" - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_proposal.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_proposal.py deleted file mode 100644 index 05416c54b1..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_proposal.py +++ /dev/null @@ -1,121 +0,0 @@ -"""A credential proposal content message.""" - -from typing import Optional - -from marshmallow import EXCLUDE, fields - -from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from .....messaging.valid import ( - INDY_CRED_DEF_ID_EXAMPLE, - INDY_CRED_DEF_ID_VALIDATE, - INDY_DID_EXAMPLE, - INDY_DID_VALIDATE, - INDY_SCHEMA_ID_EXAMPLE, - INDY_SCHEMA_ID_VALIDATE, - INDY_VERSION_EXAMPLE, - INDY_VERSION_VALIDATE, -) -from ..message_types import CREDENTIAL_PROPOSAL, PROTOCOL_PACKAGE -from .inner.credential_preview import CredentialPreview, CredentialPreviewSchema - -HANDLER_CLASS = ( - f"{PROTOCOL_PACKAGE}.handlers.credential_proposal_handler.CredentialProposalHandler" -) - - -class CredentialProposal(AgentMessage): - """Class representing a credential proposal.""" - - class Meta: - """CredentialProposal metadata.""" - - handler_class = HANDLER_CLASS - schema_class = "CredentialProposalSchema" - message_type = CREDENTIAL_PROPOSAL - - def __init__( - self, - _id: Optional[str] = None, - *, - comment: Optional[str] = None, - credential_proposal: Optional[CredentialPreview] = None, - schema_id: Optional[str] = None, - schema_issuer_did: Optional[str] = None, - schema_name: Optional[str] = None, - schema_version: Optional[str] = None, - cred_def_id: Optional[str] = None, - issuer_did: Optional[str] = None, - **kwargs, - ): - """Initialize credential proposal object. - - Args: - comment: optional human-readable comment - credential_proposal: proposed credential preview - schema_id: schema identifier - schema_issuer_did: schema issuer DID - schema_name: schema name - schema_version: schema version - cred_def_id: credential definition identifier - issuer_did: credential issuer DID - kwargs: additional key-value arguments to map into message class properties - """ - super().__init__(_id, **kwargs) - self.comment = comment - self.credential_proposal = credential_proposal - self.schema_id = schema_id - self.schema_issuer_did = schema_issuer_did - self.schema_name = schema_name - self.schema_version = schema_version - self.cred_def_id = cred_def_id - self.issuer_did = issuer_did - - -class CredentialProposalSchema(AgentMessageSchema): - """Credential proposal schema.""" - - class Meta: - """Credential proposal schema metadata.""" - - model_class = CredentialProposal - unknown = EXCLUDE - - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - credential_proposal = fields.Nested( - CredentialPreviewSchema, required=False, allow_none=False - ) - schema_id = fields.Str( - required=False, - allow_none=False, - validate=INDY_SCHEMA_ID_VALIDATE, - metadata={"example": INDY_SCHEMA_ID_EXAMPLE}, - ) - schema_issuer_did = fields.Str( - required=False, - allow_none=False, - validate=INDY_DID_VALIDATE, - metadata={"example": INDY_DID_EXAMPLE}, - ) - schema_name = fields.Str(required=False, allow_none=False) - schema_version = fields.Str( - required=False, - allow_none=False, - validate=INDY_VERSION_VALIDATE, - metadata={"example": INDY_VERSION_EXAMPLE}, - ) - cred_def_id = fields.Str( - required=False, - allow_none=False, - validate=INDY_CRED_DEF_ID_VALIDATE, - metadata={"example": INDY_CRED_DEF_ID_EXAMPLE}, - ) - issuer_did = fields.Str( - required=False, - allow_none=False, - validate=INDY_DID_VALIDATE, - metadata={"example": INDY_DID_EXAMPLE}, - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_request.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_request.py deleted file mode 100644 index 5ae6deccc4..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/credential_request.py +++ /dev/null @@ -1,83 +0,0 @@ -"""A credential request content message.""" - -from typing import Optional, Sequence - -from marshmallow import EXCLUDE, fields - -from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from .....messaging.decorators.attach_decorator import ( - AttachDecorator, - AttachDecoratorSchema, -) -from ..message_types import ATTACH_DECO_IDS, CREDENTIAL_REQUEST, PROTOCOL_PACKAGE - -HANDLER_CLASS = ( - f"{PROTOCOL_PACKAGE}.handlers.credential_request_handler.CredentialRequestHandler" -) - - -class CredentialRequest(AgentMessage): - """Class representing a credential request.""" - - class Meta: - """CredentialRequest metadata.""" - - handler_class = HANDLER_CLASS - schema_class = "CredentialRequestSchema" - message_type = CREDENTIAL_REQUEST - - def __init__( - self, - _id: Optional[str] = None, - *, - comment: Optional[str] = None, - requests_attach: Sequence[AttachDecorator] = None, - **kwargs, - ): - """Initialize credential request object. - - Args: - comment: optional comment - requests_attach: requests attachments - kwargs: additional key-value arguments to map into message class properties - - """ - super().__init__(_id=_id, **kwargs) - self.comment = comment - self.requests_attach = list(requests_attach) if requests_attach else [] - - def indy_cred_req(self, index: int = 0): - """Retrieve and decode indy credential request from attachment. - - Args: - index: ordinal in attachment list to decode and return - (typically, list has length 1) - - """ - return self.requests_attach[index].content - - @classmethod - def wrap_indy_cred_req(cls, indy_cred_req: dict) -> AttachDecorator: - """Convert an indy credential request to an attachment decorator.""" - return AttachDecorator.data_base64( - mapping=indy_cred_req, ident=ATTACH_DECO_IDS[CREDENTIAL_REQUEST] - ) - - -class CredentialRequestSchema(AgentMessageSchema): - """Credential request schema.""" - - class Meta: - """Credential request schema metadata.""" - - model_class = CredentialRequest - unknown = EXCLUDE - - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - requests_attach = fields.Nested( - AttachDecoratorSchema, required=True, many=True, data_key="requests~attach" - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/inner/__init__.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/inner/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/inner/credential_preview.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/inner/credential_preview.py deleted file mode 100644 index 9a2e75b06b..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/inner/credential_preview.py +++ /dev/null @@ -1,186 +0,0 @@ -"""A credential preview inner object.""" - -from typing import Optional, Sequence - -from marshmallow import EXCLUDE, fields - -from ......messaging.models.base import BaseModel, BaseModelSchema -from ......wallet.util import b64_to_str -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import CREDENTIAL_PREVIEW - - -class CredAttrSpec(BaseModel): - """Class representing a preview of an attibute.""" - - class Meta: - """Attribute preview metadata.""" - - schema_class = "CredAttrSpecSchema" - - def __init__( - self, *, name: str, value: str, mime_type: Optional[str] = None, **kwargs - ): - """Initialize attribute preview object. - - Args: - name: attribute name - value: attribute value; caller must base64-encode for attributes with - non-empty MIME type - mime_type: MIME type (default null) - kwargs: additional keyword arguments to map into message class properties - - """ - super().__init__(**kwargs) - - self.name = name - self.value = value - self.mime_type = mime_type.lower() if mime_type else None - - @staticmethod - def list_plain(plain: dict): - """Return a list of `CredAttrSpec` without MIME types from names/values. - - Args: - plain: dict mapping names to values - - Returns: - List of CredAttrSpecs with no MIME types - - """ - return [CredAttrSpec(name=k, value=plain[k]) for k in plain] - - def b64_decoded_value(self) -> str: - """Value, base64-decoded if applicable.""" - - return b64_to_str(self.value) if self.value and self.mime_type else self.value - - def __eq__(self, other): - """Equality comparator.""" - - if self.name != other.name: - return False # distinct attribute names - - if self.mime_type != other.mime_type: - return False # distinct MIME types - - return self.b64_decoded_value() == other.b64_decoded_value() - - -class CredAttrSpecSchema(BaseModelSchema): - """Attribute preview schema.""" - - class Meta: - """Attribute preview schema metadata.""" - - model_class = CredAttrSpec - unknown = EXCLUDE - - name = fields.Str( - required=True, - metadata={"description": "Attribute name", "example": "favourite_drink"}, - ) - mime_type = fields.Str( - required=False, - data_key="mime-type", - allow_none=True, - metadata={ - "description": "MIME type: omit for (null) default", - "example": "image/jpeg", - }, - ) - value = fields.Str( - required=True, - metadata={ - "description": "Attribute value: base64-encode if MIME type is present", - "example": "martini", - }, - ) - - -class CredentialPreview(BaseModel): - """Class representing a credential preview inner object.""" - - class Meta: - """Credential preview metadata.""" - - schema_class = "CredentialPreviewSchema" - message_type = CREDENTIAL_PREVIEW - - def __init__( - self, - *, - _type: Optional[str] = None, - attributes: Sequence[CredAttrSpec] = None, - **kwargs, - ): - """Initialize credential preview object. - - Args: - _type: formalism for Marshmallow model creation: ignored - attributes (list): list of attribute preview dicts; e.g., [ - { - "name": "attribute_name", - "value": "value" - }, - { - "name": "icon", - "mime-type": "image/png", - "value": "cG90YXRv" - } - ] - kwargs: additional keyword arguments to map into message class properties - - """ - super().__init__(**kwargs) - self.attributes = list(attributes) if attributes else [] - - @property - def _type(self): - """Accessor for message type.""" - return DIDCommPrefix.qualify_current(CredentialPreview.Meta.message_type) - - def attr_dict(self, decode: bool = False): - """Return name:value pair per attribute. - - Args: - decode: whether first to decode attributes with MIME type - - """ - - return { - attr.name: ( - b64_to_str(attr.value) if attr.mime_type and decode else attr.value - ) - for attr in self.attributes - } - - def mime_types(self): - """Return per-attribute mapping from name to MIME type. - - Return empty dict if no attribute has MIME type. - - """ - return {attr.name: attr.mime_type for attr in self.attributes if attr.mime_type} - - -class CredentialPreviewSchema(BaseModelSchema): - """Credential preview schema.""" - - class Meta: - """Credential preview schema metadata.""" - - model_class = CredentialPreview - unknown = EXCLUDE - - _type = fields.Str( - required=False, - data_key="@type", - metadata={ - "description": "Message type identifier", - "example": CREDENTIAL_PREVIEW, - }, - ) - attributes = fields.Nested( - CredAttrSpecSchema, many=True, required=True, data_key="attributes" - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/inner/tests/__init__.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/inner/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/inner/tests/test_credential_preview.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/inner/tests/test_credential_preview.py deleted file mode 100644 index 27adfb5b23..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/inner/tests/test_credential_preview.py +++ /dev/null @@ -1,103 +0,0 @@ -from unittest import TestCase - -from ......didcomm_prefix import DIDCommPrefix -from ....message_types import CREDENTIAL_PREVIEW -from ..credential_preview import CredAttrSpec, CredentialPreview - -CRED_PREVIEW = CredentialPreview( - attributes=( - CredAttrSpec.list_plain({"test": "123", "hello": "world"}) - + [CredAttrSpec(name="icon", value="cG90YXRv", mime_type="image/PNG")] - ) -) - - -class TestCredAttrSpec(TestCase): - """Attribute preview tests""" - - def test_eq(self): - attr_previews_none_plain = [ - CredAttrSpec(name="item", value="value"), - CredAttrSpec(name="item", value="value", mime_type=None), - ] - attr_previews_different = [ - CredAttrSpec(name="item", value="dmFsdWU=", mime_type="image/png"), - CredAttrSpec(name="item", value="distinct value"), - CredAttrSpec(name="distinct_name", value="distinct value", mime_type=None), - ] - - for lhs in attr_previews_none_plain: - for rhs in attr_previews_different: - assert lhs != rhs - - for lidx in range(len(attr_previews_none_plain) - 1): - for ridx in range(lidx + 1, len(attr_previews_none_plain)): - assert attr_previews_none_plain[lidx] == attr_previews_none_plain[ridx] - - for lidx in range(len(attr_previews_different) - 1): - for ridx in range(lidx + 1, len(attr_previews_different)): - assert attr_previews_different[lidx] != attr_previews_different[ridx] - - -class TestCredentialPreview(TestCase): - """Presentation preview tests.""" - - def test_init(self): - """Test initializer.""" - assert CRED_PREVIEW.attributes - - def test_type(self): - """Test type.""" - assert CRED_PREVIEW._type == DIDCommPrefix.qualify_current(CREDENTIAL_PREVIEW) - - def test_preview(self): - """Test preview for attr-dict and metadata utilities.""" - assert CRED_PREVIEW.attr_dict(decode=False) == { - "test": "123", - "hello": "world", - "icon": "cG90YXRv", - } - assert CRED_PREVIEW.attr_dict(decode=True) == { - "test": "123", - "hello": "world", - "icon": "potato", - } - assert CRED_PREVIEW.mime_types() == { - "icon": "image/png" # canonicalize to lower case - } - - def test_deserialize(self): - """Test deserialize.""" - obj = { - "@type": CREDENTIAL_PREVIEW, - "attributes": [ - {"name": "name", "value": "Alexander Delarge"}, - {"name": "pic", "mime-type": "image/png", "value": "Abcd0123..."}, - ], - } - - cred_preview = CredentialPreview.deserialize(obj) - assert type(cred_preview) is CredentialPreview - - def test_serialize(self): - """Test serialization.""" - - cred_preview_dict = CRED_PREVIEW.serialize() - assert cred_preview_dict == { - "@type": DIDCommPrefix.qualify_current(CREDENTIAL_PREVIEW), - "attributes": [ - {"name": "test", "value": "123"}, - {"name": "hello", "value": "world"}, - {"name": "icon", "mime-type": "image/png", "value": "cG90YXRv"}, - ], - } - - -class TestCredentialPreviewSchema(TestCase): - """Test credential cred preview schema.""" - - def test_make_model(self): - """Test making model.""" - data = CRED_PREVIEW.serialize() - model_instance = CredentialPreview.deserialize(data) - assert isinstance(model_instance, CredentialPreview) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/__init__.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_ack.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_ack.py deleted file mode 100644 index 0a66c27bfb..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_ack.py +++ /dev/null @@ -1,51 +0,0 @@ -from unittest import TestCase, mock - -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import CREDENTIAL_ACK, PROTOCOL_PACKAGE -from ..credential_ack import CredentialAck - - -class TestCredentialAck(TestCase): - """Credential ack tests""" - - def test_init(self): - """Test initializer""" - credential_ack = CredentialAck() - - def test_type(self): - """Test type""" - credential_ack = CredentialAck() - - assert credential_ack._type == DIDCommPrefix.qualify_current(CREDENTIAL_ACK) - - @mock.patch(f"{PROTOCOL_PACKAGE}.messages.credential_ack.CredentialAckSchema.load") - def test_deserialize(self, mock_credential_ack_schema_load): - """ - Test deserialize - """ - obj = CredentialAck() - - credential_ack = CredentialAck.deserialize(obj) - mock_credential_ack_schema_load.assert_called_once_with(obj) - - assert credential_ack is mock_credential_ack_schema_load.return_value - - @mock.patch(f"{PROTOCOL_PACKAGE}.messages.credential_ack.CredentialAckSchema.dump") - def test_serialize(self, mock_credential_ack_schema_dump): - """ - Test serialization. - """ - obj = CredentialAck() - - credential_ack_dict = obj.serialize() - mock_credential_ack_schema_dump.assert_called_once_with(obj) - - assert credential_ack_dict is mock_credential_ack_schema_dump.return_value - - def test_make_model(self): - """Test making model.""" - - credential_ack = CredentialAck() - data = credential_ack.serialize() - model_instance = CredentialAck.deserialize(data) - assert isinstance(model_instance, CredentialAck) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_issue.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_issue.py deleted file mode 100644 index ede71853d1..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_issue.py +++ /dev/null @@ -1,147 +0,0 @@ -from unittest import TestCase, mock - -from ......messaging.decorators.attach_decorator import AttachDecorator -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import ATTACH_DECO_IDS, CREDENTIAL_ISSUE, PROTOCOL_PACKAGE -from ..credential_issue import CredentialIssue - - -class TestCredentialIssue(TestCase): - """Credential issue tests""" - - indy_cred = { - "schema_id": "LjgpST2rjsoxYegQDRm7EL:2:bc-reg:1.0", - "cred_def_id": "LjgpST2rjsoxYegQDRm7EL:3:CL:18:tag", - "rev_reg_id": "LjgpST2rjsoxYegQDRm7EL:4:LjgpST2rjsoxYegQDRm7EL:3:CL:18:tag:CL_ACCUM:1", - "values": { - "busId": {"raw": "11155555", "encoded": "11155555"}, - "legalName": { - "raw": "Babka Galaxy", - "encoded": "107723975795096474174315415205901102419879622561395089750910511985549475735747", - }, - "id": {"raw": "5", "encoded": "5"}, - "orgTypeId": {"raw": "1", "encoded": "1"}, - "effectiveDate": { - "raw": "2012-12-01", - "encoded": "58785836675119218543950531421539993546216494060018521243314445986885543138388", - }, - "jurisdictionId": {"raw": "1", "encoded": "1"}, - "endDate": { - "raw": "", - "encoded": "102987336249554097029535212322581322789799900648198034993379397001115665086549", - }, - }, - "signature": { - "p_credential": { - "m_2": "60025883287089799626689274984362649922028954710702989273350424792094051625907", - "a": "33574785085847496372223801384241174668280696192852342004649681358898319989377891201713237406189930904621943660579244780378356431325594072391319837474469436200535615918847408676250915598611100068705846552950672619639766733118699744590194148554187848404028169947572858712592004307286251531728499790515868404251079046925435202101170698552776314885035743276729493940581544827310348632105741785505818500141788882165796461479904049413245974826370118124656594309043126033311790481868941737635314924873471152593101941520014919522243774177999183508913726745154494726830096189641688720673911842149721875115446765101254783088102", - "e": "259344723055062059907025491480697571938277889515152306249728583105665800713306759149981690559193987143012367913206299323899696942213235956742929940839890541204554505134958365542601", - "v": "8609087712648327689510560843448768242969198387856549646434987127729892694214386082710530362693226591495343780017066542203667948482019255226968628218013767981247576292730389932608795727994162072985790185993138122475561426334951896920290599111436791225402577204027790420706987810169826735050717355066696030347321187354133263894735515127702270039945304850524250402144664403971571904353156572222923701680935669167750650688016372444804704998087365054978152701248950729399377780813365024757989269208934482967970445445223084620917624825052959697120057360426040239100930790635416973591134497181715131476498510569905885753432826750000829362210364061766697316138646771666357343198925355584209303847699218225254051213598531538421032318684976506329062116913654998320196203740062523483508588929287294193683755114531891923195772740958", - }, - "r_credential": { - "sigma": "1 00F38C50E192DAF9133130888DA4A3291754B1A7D09A7DCCDD408D4E13F57267 1 0C6C9D8510580A8C9D8F0E21F51FF76E8F1419C2C909BBB9761AD9E75E46517F 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", - "c": "12F8B7BD08471C27F6AF8EE06374D200FCEA61718FACA61FD8B90EEED7A11AD6", - "vr_prime_prime": "103015BFD51C02121DF61993973F312D5972EFF3B3B1B80BC614D5A747510366", - "witness_signature": { - "sigma_i": "1 165767F82FF8FD92237985441D2C758706A5EC1D21FBEF8611C6AC4E3CAD10DA 1 1FC786E5CD2D8B30F1C567579B4EC143C5951B7464F78B86A03419CB335EA81B 1 0B1A1356056BEDF9C61AE2D66FF0405E3B1D934DAC97099BDF6AC3ECCBFAF745 1 106B15BC294810EEDF8AD363A85CC8ECC8AA061538BB31BAE5252377D77E7FA3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", - "u_i": "1 017A61B7C8B5B80EB245BE6788A28F926D8CBB9829E657D437640EF09ACD0C80 1 1AF4229C05C728AEAEEE6FC411B357B857E773BA79FF677373A6BE8F60C02C3A 1 10CB82C4913E2324C06164BF22A2BD38CEE528C797C55061C2D2486C3F6BF747 1 116CE544B1CB99556BFC0621C57C3D9F2B78D034946322EEA218DFDBDD940EA3 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8 1 0000000000000000000000000000000000000000000000000000000000000000", - "g_i": "1 0042BF46E9BAE9696F394FE7C26AFDE3C8963A2A0658D4C32737405F1576EB46 1 0194E97A9D92D46AAD61DAE06926D3361F531EB10D03C7520F3BD69D3E49311C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", - }, - "g_i": "1 0042BF46E9BAE9696F394FE7C26AFDE3C8963A2A0658D4C32737405F1576EB46 1 0194E97A9D92D46AAD61DAE06926D3361F531EB10D03C7520F3BD69D3E49311C 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", - "i": 1, - "m2": "84B5722AE3A1CF27CB1EA56CD33D289CB87A4401C6B103D0D7B7EA869DAF6BB3", - }, - }, - "signature_correctness_proof": { - "se": "19792617148120152105226254239016588540058878757479987545108556827210662529343348161518678852958020771878595740749192412985440625444455760950622452787061547854765389520937092533324699495837410270589105368479415954380927050080439536019149709356488657394895381670676082762285043378943096265107585990717517541825549361747506315768406364562926877132553754434293723146759285511815164904802662712140021121638529229138315163496513377824821704164701067409581646133944445999621553849950380606679724798867481070896073389886302519310697801643262282687875393404841657943289557895895565050618203027512724917946512514235898009424924", - "c": "20346348618412341786428948997994890734628812067145521907471418530511751955386", - }, - "rev_reg": { - "accum": "21 12E821764448DE2B5754DEC16864096CFAE4BB68D4DC0CE3E5C4849FC7CBCCC0C 21 11677132B2DFB0C291D0616811BF2AC0CD464A35FF6927B821A5EACF24D94F3A5 6 5471991A0950DBD431A4DD86A8AD101E033AB5EBC29A97CAFE0E4F2C426F5821 4 1B34A4C75174974A698061A09AFFED62B78AC2AAF876BF7788BAF3FC9A8B47DF 6 7D7C5E96AE17DDB21EC98378E3185707A69CF86426F5526C9A55D1FAA2F6FA83 4 277100094333E24170CD3B020B0C91A7E9510F69218AD96AC966565AEF66BC71" - }, - "witness": { - "omega": "21 136960A5E73C494F007BFE156889137E8B6DF301D5FF673C410CEE0F14AFAF1AE 21 132D4BA49C6BD8AB3CF52929D115976ABB1785D288F311CBB4455A85D07E2568C 6 70E7C40BA4F607262697556BB17FA6C85E9C188FA990264F4F031C39B5811239 4 351B98620B239DF14F3AB0B754C70597035A3B099D287A9855D11C55BA9F0C16 6 8AA1C473D792DF4F8287D0A93749046385CE411AAA1D685AA3C874C15B8628DB 4 0D6491BF5F127C1A0048CF137AEE17B62F4E49F3BDD9ECEBD14D56C43D211544" - }, - } - - cred_issue = CredentialIssue( - comment="Test", - credentials_attach=[ - AttachDecorator.data_base64( - mapping=indy_cred, - ident=ATTACH_DECO_IDS[CREDENTIAL_ISSUE], - ) - ], - ) - - def test_init(self): - """Test initializer""" - credential_issue = CredentialIssue( - comment="Test", - credentials_attach=[ - AttachDecorator.data_base64( - mapping=self.indy_cred, - ident=ATTACH_DECO_IDS[CREDENTIAL_ISSUE], - ) - ], - ) - assert credential_issue.credentials_attach[0].content == self.indy_cred - assert credential_issue.credentials_attach[0].ident # auto-generates UUID4 - assert credential_issue.indy_credential(0) == self.indy_cred - - def test_type(self): - """Test type""" - credential_issue = CredentialIssue( - comment="Test", - credentials_attach=[ - AttachDecorator.data_base64( - mapping=self.indy_cred, - ident=ATTACH_DECO_IDS[CREDENTIAL_ISSUE], - ) - ], - ) - - assert credential_issue._type == DIDCommPrefix.qualify_current(CREDENTIAL_ISSUE) - - @mock.patch( - f"{PROTOCOL_PACKAGE}.messages.credential_issue.CredentialIssueSchema.load" - ) - def test_deserialize(self, mock_credential_issue_schema_load): - """ - Test deserialize - """ - obj = self.cred_issue - - credential_issue = CredentialIssue.deserialize(obj) - mock_credential_issue_schema_load.assert_called_once_with(obj) - - assert credential_issue is mock_credential_issue_schema_load.return_value - - @mock.patch( - f"{PROTOCOL_PACKAGE}.messages.credential_issue.CredentialIssueSchema.dump" - ) - def test_serialize(self, mock_credential_issue_schema_dump): - """ - Test serialization. - """ - obj = self.cred_issue - - credential_issue_dict = obj.serialize() - mock_credential_issue_schema_dump.assert_called_once_with(obj) - - assert credential_issue_dict is mock_credential_issue_schema_dump.return_value - - -class TestCredentialIssueSchema(TestCase): - """Test credential cred issue schema""" - - credential_issue = CredentialIssue( - comment="Test", - credentials_attach=[AttachDecorator.data_base64({"hello": "world"})], - ) - - def test_make_model(self): - """Test making model.""" - data = self.credential_issue.serialize() - model_instance = CredentialIssue.deserialize(data) - assert isinstance(model_instance, CredentialIssue) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_offer.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_offer.py deleted file mode 100644 index a4f1b1f63e..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_offer.py +++ /dev/null @@ -1,134 +0,0 @@ -from unittest import TestCase, mock - -from ......messaging.decorators.attach_decorator import AttachDecorator -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import ATTACH_DECO_IDS, CREDENTIAL_OFFER, PROTOCOL_PACKAGE -from ..credential_offer import CredentialOffer -from ..inner.credential_preview import CredAttrSpec, CredentialPreview - - -class TestCredentialOffer(TestCase): - """Credential offer tests""" - - indy_offer = { - "nonce": "614100168443907415054289", - "schema_id": "GMm4vMw8LLrLJjp81kRRLp:2:drinks:1.0", - "cred_def_id": "GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag", - "key_correctness_proof": { - "c": "56585275561905717161839952743647026395542989876162452893531670700564212393854", - "xz_cap": "287165348340975789727971384349378142287959058342225940074538845935436773874329328991029519089234724427434486533815831859470220965864684738156552672305499237703498538513271159520247291220966466227432832227063850459130839372368207180055849729905609985998538537568736869293407524473496064816314603497171002962615258076622463931743189286900496109205216280607630768503576692684508445827948798897460776289403908388023698817549732288585499518794247355928287227786391839285875542402023633857234040392852972008208415080178060364227401609053629562410235736217349229809414782038441992275607388974250885028366550369268002576993725278650846144639865881671599246472739142902676861561148712912969364530598265", - "xr_cap": [ - [ - "member", - "247173988424242283128308731284354519593625104582055668969315003963838548670841899501658349312938942946846730152870858369236571789232183841781453957957720697180067746500659257059976519795874971348181945469064991738990738845965440847535223580150468375375443512237424530837415294161162638683584221123453778375487245671372772618360172541002473454666729113558205280977594339672398197686260680189972481473789054636358472310216645491588945137379027958712059669609528877404178425925715596671339305959202588832885973524555444251963470084399490131160758976923444260763440975941005911948597957705824445435191054260665559130246488082450660079956928491647323363710347167509227696874201965902602039122291827", - ], - [ - "favourite", - "1335045667644070498565118732156146549025899560440568943935771536511299164006020730238478605099548137764051990321413418863325926730012675851687537953795507658228985382833693223549386078823801188511091609027561372137859781602606173745112393410558404328055415428275164533367998547196095783458226529569321865083846885509205360165413682408429660871664533434140200342530874654054024409641491095797032894595844264175356021739370667850887453108137634226023771337973520900908849320630756049969052968900455735023806005098461831167599998292029791540116613937132049776519811961709679592741659868352478832873910002910063294074562896887581629929595271513565238416621119418443383796085468376565042025935483490", - ], - [ - "master_secret", - "1033992860010367458372180504097559955661066772142722707045156268794833109485917658718054000138242001598760494274716663669095123169580783916372365989852993328621834238281615788751278692675115165487417933883883618299385468584923910731758768022514670608541825229491053331942365151645754250522222493603795702384546708563091580112967031435038732735155283423684631622768416201085577137158105343396606143962017453945220908112975903537378485103755718950361047334234687103399968712220979025991673471498636490232494897885460464490635716242509247751966176791851396526210422140145723375747195416033531994076204650208879292521201294795264925045126704368284107432921974127792914580116411247536542717749670349", - ], - ], - }, - } - preview = CredentialPreview( - attributes=CredAttrSpec.list_plain( - {"member": "James Bond", "favourite": "martini"} - ) - ) - offer = CredentialOffer( - comment="shaken, not stirred", - credential_preview=preview, - offers_attach=[ - AttachDecorator.data_base64( - mapping=indy_offer, - ident=ATTACH_DECO_IDS[CREDENTIAL_OFFER], - ) - ], - ) - - def test_init(self): - """Test initializer""" - credential_offer = CredentialOffer( - comment="shaken, not stirred", - credential_preview=self.preview, - offers_attach=[ - AttachDecorator.data_base64( - mapping=self.indy_offer, - ident=ATTACH_DECO_IDS[CREDENTIAL_OFFER], - ) - ], - ) - assert credential_offer.credential_preview == self.preview - assert credential_offer.offers_attach[0].content == self.indy_offer - assert credential_offer.indy_offer(0) == self.indy_offer - - def test_type(self): - """Test type""" - credential_offer = CredentialOffer( - comment="shaken, not stirred", - credential_preview=self.preview, - offers_attach=[ - AttachDecorator.data_base64( - mapping=self.indy_offer, - ident=ATTACH_DECO_IDS[CREDENTIAL_OFFER], - ) - ], - ) - - assert credential_offer._type == DIDCommPrefix.qualify_current(CREDENTIAL_OFFER) - - @mock.patch( - f"{PROTOCOL_PACKAGE}.messages.credential_offer.CredentialOfferSchema.load" - ) - def test_deserialize(self, mock_credential_offer_schema_load): - """ - Test deserialize - """ - obj = self.indy_offer - - credential_offer = CredentialOffer.deserialize(obj) - mock_credential_offer_schema_load.assert_called_once_with(obj) - - assert credential_offer is mock_credential_offer_schema_load.return_value - - @mock.patch( - f"{PROTOCOL_PACKAGE}.messages.credential_offer.CredentialOfferSchema.dump" - ) - def test_serialize(self, mock_credential_offer_schema_dump): - """ - Test serialization. - """ - credential_offer = CredentialOffer( - comment="shaken, not stirred", - credential_preview=self.preview, - offers_attach=[ - AttachDecorator.data_base64( - mapping=self.indy_offer, - ident=ATTACH_DECO_IDS[CREDENTIAL_OFFER], - ) - ], - ) - - credential_offer_dict = credential_offer.serialize() - mock_credential_offer_schema_dump.assert_called_once_with(credential_offer) - - assert credential_offer_dict is mock_credential_offer_schema_dump.return_value - - -class TestCredentialOfferSchema(TestCase): - """Test credential cred offer schema""" - - credential_offer = CredentialOffer( - comment="shaken, not stirred", - credential_preview=TestCredentialOffer.preview, - offers_attach=[AttachDecorator.data_base64(TestCredentialOffer.indy_offer)], - ) - - def test_make_model(self): - """Test making model.""" - data = self.credential_offer.serialize() - model_instance = CredentialOffer.deserialize(data) - assert isinstance(model_instance, CredentialOffer) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_problem_report.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_problem_report.py deleted file mode 100644 index 2e2e5eb02a..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_problem_report.py +++ /dev/null @@ -1,103 +0,0 @@ -from unittest import TestCase, mock - -import pytest - -from ......messaging.models.base import BaseModelError -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import CREDENTIAL_PROBLEM_REPORT, PROTOCOL_PACKAGE -from .. import credential_problem_report as test_module -from ..credential_problem_report import ( - CredentialProblemReport, - CredentialProblemReportSchema, - ProblemReportReason, - ValidationError, -) - - -class TestCredentialProblemReport(TestCase): - """Problem report tests.""" - - def test_init_type(self): - """Test initializer.""" - - prob = CredentialProblemReport( - description={ - "en": "oh no", - "code": ProblemReportReason.ISSUANCE_ABANDONED.value, - } - ) - assert prob._type == DIDCommPrefix.qualify_current(CREDENTIAL_PROBLEM_REPORT) - - @mock.patch( - f"{PROTOCOL_PACKAGE}.messages.credential_problem_report." - "CredentialProblemReportSchema.load" - ) - def test_deserialize(self, mock_load): - """Test deserialization.""" - - obj = CredentialProblemReport( - description={ - "en": "oh no", - "code": ProblemReportReason.ISSUANCE_ABANDONED.value, - } - ) - - prob = CredentialProblemReport.deserialize(obj) - mock_load.assert_called_once_with(obj) - - assert prob is mock_load.return_value - - @mock.patch( - f"{PROTOCOL_PACKAGE}.messages.credential_problem_report." - "CredentialProblemReportSchema.dump" - ) - def test_serialize(self, mock_dump): - """Test serialization.""" - - obj = CredentialProblemReport( - description={ - "en": "oh no", - "code": ProblemReportReason.ISSUANCE_ABANDONED.value, - } - ) - - ser = obj.serialize() - mock_dump.assert_called_once_with(obj) - - assert ser is mock_dump.return_value - - def test_make_model(self): - """Test making model.""" - - prob = CredentialProblemReport( - description={ - "en": "oh no", - "code": ProblemReportReason.ISSUANCE_ABANDONED.value, - } - ) - data = prob.serialize() - model_instance = CredentialProblemReport.deserialize(data) - assert isinstance(model_instance, CredentialProblemReport) - - prob = CredentialProblemReport() - data = prob.serialize() - with pytest.raises(BaseModelError): - CredentialProblemReport.deserialize(data) - - def test_validate_x(self): - """Exercise validation requirements.""" - schema = CredentialProblemReportSchema() - with pytest.raises(ValidationError): - schema.validate_fields({}) - - def test_validate_and_logger(self): - """Capture ValidationError and Logs.""" - data = CredentialProblemReport( - description={ - "en": "oh no", - "code": "invalid_code", - }, - ).serialize() - with mock.patch.object(test_module, "LOGGER", autospec=True) as mock_logger: - CredentialProblemReportSchema().validate_fields(data) - assert mock_logger.warning.call_count == 1 diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_proposal.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_proposal.py deleted file mode 100644 index 8050ffecb3..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_proposal.py +++ /dev/null @@ -1,145 +0,0 @@ -from unittest import TestCase - -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import CREDENTIAL_PREVIEW, CREDENTIAL_PROPOSAL -from ..credential_proposal import CredentialProposal -from ..inner.credential_preview import CredAttrSpec, CredentialPreview - -CRED_PREVIEW = CredentialPreview( - attributes=( - CredAttrSpec.list_plain({"test": "123", "hello": "world"}) - + [CredAttrSpec(name="icon", value="cG90YXRv", mime_type="image/png")] - ) -) - - -class TestCredentialProposal(TestCase): - """Credential proposal tests.""" - - def test_init(self): - """Test initializer.""" - credential_proposal = CredentialProposal( - comment="Hello World", - credential_proposal=CRED_PREVIEW, - schema_id="GMm4vMw8LLrLJjp81kRRLp:2:ahoy:1560364003.0", - cred_def_id="GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag", - ) - assert credential_proposal.credential_proposal == CRED_PREVIEW - - def test_type(self): - """Test type.""" - credential_proposal = CredentialProposal( - comment="Hello World", - credential_proposal=CRED_PREVIEW, - schema_id="GMm4vMw8LLrLJjp81kRRLp:2:ahoy:1560364003.0", - cred_def_id="GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag", - ) - - assert credential_proposal._type == DIDCommPrefix.qualify_current( - CREDENTIAL_PROPOSAL - ) - - def test_deserialize(self): - """Test deserialize.""" - obj = { - "comment": "Hello World", - "credential_proposal": { - "@type": DIDCommPrefix.qualify_current(CREDENTIAL_PREVIEW), - "attributes": [ - {"name": "name", "value": "Alexander Delarge"}, - {"name": "pic", "mime-type": "image/png", "value": "Abcd0123..."}, - ], - }, - "schema_id": "GMm4vMw8LLrLJjp81kRRLp:2:ahoy:1560364003.0", - "cred_def_id": "GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag", - } - - cred_proposal = CredentialProposal.deserialize(obj) - assert type(cred_proposal) is CredentialProposal - - def test_serialize(self): - """Test serialization.""" - - cred_proposal = CredentialProposal( - comment="Hello World", - credential_proposal=CRED_PREVIEW, - schema_id="GMm4vMw8LLrLJjp81kRRLp:2:ahoy:1560364003.0", - cred_def_id="GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag", - ) - - cred_proposal_dict = cred_proposal.serialize() - cred_proposal_dict.pop("@id") - - assert cred_proposal_dict == { - "@type": DIDCommPrefix.qualify_current(CREDENTIAL_PROPOSAL), - "comment": "Hello World", - "credential_proposal": { - "@type": DIDCommPrefix.qualify_current(CREDENTIAL_PREVIEW), - "attributes": [ - {"name": "test", "value": "123"}, - {"name": "hello", "value": "world"}, - {"name": "icon", "mime-type": "image/png", "value": "cG90YXRv"}, - ], - }, - "schema_id": "GMm4vMw8LLrLJjp81kRRLp:2:ahoy:1560364003.0", - "cred_def_id": "GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag", - } - - def test_serialize_no_proposal(self): - """Test serialization.""" - - cred_proposal = CredentialProposal( - comment="Hello World", - credential_proposal=None, - schema_id="GMm4vMw8LLrLJjp81kRRLp:2:ahoy:1560364003.0", - cred_def_id="GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag", - ) - - cred_proposal_dict = cred_proposal.serialize() - cred_proposal_dict.pop("@id") - - assert cred_proposal_dict == { - "@type": DIDCommPrefix.qualify_current(CREDENTIAL_PROPOSAL), - "comment": "Hello World", - "schema_id": "GMm4vMw8LLrLJjp81kRRLp:2:ahoy:1560364003.0", - "cred_def_id": "GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag", - } - - -class TestCredentialProposalSchema(TestCase): - """Test credential cred proposal schema.""" - - credential_proposals = [ - CredentialProposal( - credential_proposal=CRED_PREVIEW, - ), - CredentialProposal( - comment="Hello World", - credential_proposal=CRED_PREVIEW, - cred_def_id="GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag", - ), - CredentialProposal( - comment="Hello World", - credential_proposal=CRED_PREVIEW, - schema_id="GMm4vMw8LLrLJjp81kRRLp:2:ahoy:1.0", - ), - CredentialProposal( - comment="Hello World", - credential_proposal=CRED_PREVIEW, - schema_issuer_did="GMm4vMw8LLrLJjp81kRRLp", - ), - CredentialProposal( - comment="Hello World", - credential_proposal=CRED_PREVIEW, - schema_name="ahoy", - schema_version="1.0", - issuer_did="GMm4vMw8LLrLJjp81kRRLp", - ), - ] - - def test_make_model(self): - """Test making model.""" - for credential_proposal in self.credential_proposals: - data = credential_proposal.serialize() - model_instance = CredentialProposal.deserialize(data) - assert isinstance(model_instance, CredentialProposal) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_request.py b/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_request.py deleted file mode 100644 index 4276d06480..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/messages/tests/test_credential_request.py +++ /dev/null @@ -1,123 +0,0 @@ -from unittest import TestCase, mock - -from ......messaging.decorators.attach_decorator import AttachDecorator -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import ATTACH_DECO_IDS, CREDENTIAL_REQUEST, PROTOCOL_PACKAGE -from ..credential_request import CredentialRequest - - -class TestCredentialRequest(TestCase): - """Credential request tests""" - - indy_cred_req = { - "nonce": "1017762706737386703693758", - "prover_did": "GMm4vMw8LLrLJjp81kRRLp", - "cred_def_id": "GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag", - "blinded_ms": { - "u": "83907504917598709544715660183444547664806528194879236493704185267249518487609477830252206438464922282419526404954032744426656836343614241707982523911337758117991524606767981934822739259321023980818911648706424625657217291525111737996606024710795596961607334766957629765398381678917329471919374676824400143394472619220909211861028497009707890651887260349590274729523062264675018736459760546731362496666872299645586181905130659944070279943157241097916683504866583173110187429797028853314290183583689656212022982000994142291014801654456172923356395840313420880588404326139944888917762604275764474396403919497783080752861", - "ur": "1 2422A7A25A9AB730F3399C77C28E1F6E02BB94A2C07D245B28DC4EE33E33DE49 1 1EF3FBD36FBA7510BDA79386508C0A84A33DF4171107C22895ACAE4FA4499F02 2 095E45DDF417D05FB10933FFC63D474548B7FFFF7888802F07FFFFFF7D07A8A8", - "hidden_attributes": ["master_secret"], - "committed_attributes": {}, - }, - "blinded_ms_correctness_proof": { - "c": "77782990462020711078900471139684606615516190979556618670020830699801678914552", - "v_dash_cap": "1966215015532422356590954855080129096516569112935438312989092847889400013191094311374123910677667707922694722167856889267996544544770134106600289624974901761453909338477897555013062690166110508298265469948048257876547569520215226798025984795668101468265482570744011744194025718081101032551943108999422057478928838218205736972438022128376728526831967897105301274481454020377656694232901381674223529320224276009919370080174601226836784570762698964476355045131401700464714725647784278935633253472872446202741297992383148244277451017022036452203286302631768247417186601621329239603862883753434562838622266122331169627284313213964584034951472090601638790603966977114416216909593408778336960753110805965734708636782885161632", - "m_caps": { - "master_secret": "1932933391026030434402535597188163725022560167138754201841873794167337347489231254032687761158191503499965986291267527620598858412377279828812688105949083285487853357240244045442" - }, - "r_caps": {}, - }, - } - - cred_req = CredentialRequest( - comment="Test", - requests_attach=[ - AttachDecorator.data_base64( - mapping=indy_cred_req, - ident=ATTACH_DECO_IDS[CREDENTIAL_REQUEST], - ) - ], - ) - - def test_init(self): - """Test initializer""" - credential_request = CredentialRequest( - comment="Test", - requests_attach=[ - AttachDecorator.data_base64( - mapping=self.indy_cred_req, - ident=ATTACH_DECO_IDS[CREDENTIAL_REQUEST], - ) - ], - ) - assert credential_request.requests_attach[0].content == self.indy_cred_req - assert credential_request.indy_cred_req(0) == self.indy_cred_req - - def test_type(self): - """Test type""" - credential_request = CredentialRequest( - comment="Test", - requests_attach=[ - AttachDecorator.data_base64( - mapping=self.indy_cred_req, - ident=ATTACH_DECO_IDS[CREDENTIAL_REQUEST], - ) - ], - ) - - assert credential_request._type == DIDCommPrefix.qualify_current( - CREDENTIAL_REQUEST - ) - - @mock.patch( - f"{PROTOCOL_PACKAGE}.messages." "credential_request.CredentialRequestSchema.load" - ) - def test_deserialize(self, mock_credential_request_schema_load): - """ - Test deserialize - """ - obj = self.indy_cred_req - - credential_request = CredentialRequest.deserialize(obj) - mock_credential_request_schema_load.assert_called_once_with(obj) - - assert credential_request is mock_credential_request_schema_load.return_value - - @mock.patch( - f"{PROTOCOL_PACKAGE}.messages." "credential_request.CredentialRequestSchema.dump" - ) - def test_serialize(self, mock_credential_request_schema_dump): - """ - Test serialization. - """ - credential_request = CredentialRequest( - comment="Test", - requests_attach=[ - AttachDecorator.data_base64( - mapping=self.indy_cred_req, - ident=ATTACH_DECO_IDS[CREDENTIAL_REQUEST], - ) - ], - ) - - credential_request_dict = credential_request.serialize() - mock_credential_request_schema_dump.assert_called_once_with(credential_request) - - assert credential_request_dict is mock_credential_request_schema_dump.return_value - - -class TestCredentialRequestSchema(TestCase): - """Test credential cred request schema""" - - credential_request = CredentialRequest( - comment="Test", - requests_attach=[ - AttachDecorator.data_base64(TestCredentialRequest.indy_cred_req) - ], - ) - - def test_make_model(self): - """Test making model.""" - data = self.credential_request.serialize() - model_instance = CredentialRequest.deserialize(data) - assert isinstance(model_instance, CredentialRequest) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/__init__.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/__init__.py deleted file mode 100644 index 3c9e5bb314..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Package-wide code and data.""" - -from os import environ - -UNENCRYPTED_TAGS = environ.get("EXCH_UNENCRYPTED_TAGS", "False").upper() == "TRUE" diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py deleted file mode 100644 index a73da8c01d..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/credential_exchange.py +++ /dev/null @@ -1,475 +0,0 @@ -"""Aries#0036 v1.0 credential exchange information with non-secrets storage.""" - -import logging -from typing import Any, Mapping, Optional, Union - -from marshmallow import fields, validate - -from .....core.profile import ProfileSession -from .....indy.models.cred import IndyCredential, IndyCredentialSchema -from .....indy.models.cred_abstract import IndyCredAbstract, IndyCredAbstractSchema -from .....indy.models.cred_precis import IndyCredInfo, IndyCredInfoSchema -from .....indy.models.cred_request import IndyCredRequest, IndyCredRequestSchema -from .....messaging.models.base_record import BaseExchangeRecord, BaseExchangeSchema -from .....messaging.valid import ( - INDY_CRED_DEF_ID_EXAMPLE, - INDY_CRED_DEF_ID_VALIDATE, - INDY_SCHEMA_ID_EXAMPLE, - INDY_SCHEMA_ID_VALIDATE, - UUID4_EXAMPLE, -) -from .....storage.base import StorageError -from ..messages.credential_exchange_webhook import V10CredentialExchangeWebhook -from ..messages.credential_offer import CredentialOffer, CredentialOfferSchema -from ..messages.credential_proposal import CredentialProposal, CredentialProposalSchema -from . import UNENCRYPTED_TAGS - -LOGGER = logging.getLogger(__name__) - - -class V10CredentialExchange(BaseExchangeRecord): - """Represents an Aries#0036 credential exchange.""" - - class Meta: - """CredentialExchange metadata.""" - - schema_class = "V10CredentialExchangeSchema" - - RECORD_TYPE = "credential_exchange_v10" - RECORD_ID_NAME = "credential_exchange_id" - RECORD_TOPIC = "issue_credential" - TAG_NAMES = {"~thread_id"} if UNENCRYPTED_TAGS else {"thread_id"} - - INITIATOR_SELF = "self" - INITIATOR_EXTERNAL = "external" - ROLE_ISSUER = "issuer" - ROLE_HOLDER = "holder" - - STATE_PROPOSAL_SENT = "proposal_sent" - STATE_PROPOSAL_RECEIVED = "proposal_received" - STATE_OFFER_SENT = "offer_sent" - STATE_OFFER_RECEIVED = "offer_received" - STATE_REQUEST_SENT = "request_sent" - STATE_REQUEST_RECEIVED = "request_received" - STATE_ISSUED = "credential_issued" - STATE_CREDENTIAL_RECEIVED = "credential_received" - STATE_ACKED = "credential_acked" - STATE_CREDENTIAL_REVOKED = "credential_revoked" - STATE_ABANDONED = "abandoned" - - def __init__( - self, - *, - credential_exchange_id: Optional[str] = None, - connection_id: Optional[str] = None, - thread_id: Optional[str] = None, - parent_thread_id: Optional[str] = None, - initiator: Optional[str] = None, - role: Optional[str] = None, - state: Optional[str] = None, - credential_definition_id: Optional[str] = None, - schema_id: Optional[str] = None, - credential_proposal_dict: Union[ - Mapping, CredentialProposal - ] = None, # aries message: ..._dict for historic compat on all aries msgs - credential_offer_dict: Union[Mapping, CredentialOffer] = None, # aries message - credential_offer: Union[Mapping, IndyCredAbstract] = None, # indy artifact - credential_request: Union[Mapping, IndyCredRequest] = None, # indy artifact - credential_request_metadata: Optional[Mapping] = None, - credential_id: Optional[str] = None, - raw_credential: Union[Mapping, IndyCredential] = None, # indy cred as received - credential: Union[Mapping, IndyCredInfo] = None, # indy cred as stored - revoc_reg_id: Optional[str] = None, - revocation_id: Optional[str] = None, - auto_offer: bool = False, - auto_issue: bool = False, - auto_remove: bool = True, - error_msg: Optional[str] = None, - trace: bool = False, # backward-compat: BaseRecord.from_storage() - **kwargs, - ): - """Initialize a new V10CredentialExchange.""" - super().__init__(credential_exchange_id, state, trace=trace, **kwargs) - self._id = credential_exchange_id - self.connection_id = connection_id - self.thread_id = thread_id - self.parent_thread_id = parent_thread_id - self.initiator = initiator - self.role = role - self.state = state - self.credential_definition_id = credential_definition_id - self.schema_id = schema_id - self._credential_proposal_dict = CredentialProposal.serde( - credential_proposal_dict - ) - self._credential_offer_dict = CredentialOffer.serde(credential_offer_dict) - self._credential_offer = IndyCredAbstract.serde(credential_offer) - self._credential_request = IndyCredRequest.serde(credential_request) - self.credential_request_metadata = credential_request_metadata - self.credential_id = credential_id - self._raw_credential = IndyCredential.serde(raw_credential) - self._credential = IndyCredInfo.serde(credential) - self.revoc_reg_id = revoc_reg_id - self.revocation_id = revocation_id - self.auto_offer = auto_offer - self.auto_issue = auto_issue - self.auto_remove = auto_remove - self.error_msg = error_msg - - @property - def credential_exchange_id(self) -> str: - """Accessor for the ID associated with this exchange.""" - return self._id - - @property - def credential_proposal_dict(self) -> CredentialProposal: - """Accessor; get deserialized view.""" - return ( - None - if self._credential_proposal_dict is None - else self._credential_proposal_dict.de - ) - - @credential_proposal_dict.setter - def credential_proposal_dict(self, value): - """Setter; store de/serialized views.""" - self._credential_proposal_dict = CredentialProposal.serde(value) - - @property - def credential_offer_dict(self) -> CredentialOffer: - """Accessor; get deserialized view.""" - return ( - None - if self._credential_offer_dict is None - else self._credential_offer_dict.de - ) - - @credential_offer_dict.setter - def credential_offer_dict(self, value): - """Setter; store de/serialized views.""" - self._credential_offer_dict = CredentialOffer.serde(value) - - @property - def credential_offer(self) -> IndyCredAbstract: - """Accessor; get deserialized view.""" - return None if self._credential_offer is None else self._credential_offer.de - - @credential_offer.setter - def credential_offer(self, value): - """Setter; store de/serialized views.""" - self._credential_offer = IndyCredAbstract.serde(value) - - @property - def credential_request(self) -> IndyCredRequest: - """Accessor; get deserialized view.""" - return None if self._credential_request is None else self._credential_request.de - - @credential_request.setter - def credential_request(self, value): - """Setter; store de/serialized views.""" - self._credential_request = IndyCredRequest.serde(value) - - @property - def raw_credential(self) -> IndyCredential: - """Accessor; get deserialized view.""" - return None if self._raw_credential is None else self._raw_credential.de - - @raw_credential.setter - def raw_credential(self, value): - """Setter; store de/serialized views.""" - self._raw_credential = IndyCredential.serde(value) - - @property - def credential(self) -> IndyCredInfo: - """Accessor; get deserialized view.""" - return None if self._credential is None else self._credential.de - - @credential.setter - def credential(self, value): - """Setter; store de/serialized views.""" - self._credential = IndyCredInfo.serde(value) - - async def save_error_state( - self, - session: ProfileSession, - *, - state: Optional[str] = None, - reason: Optional[str] = None, - log_params: Mapping[str, Any] = None, - log_override: bool = False, - ): - """Save record error state if need be; log and swallow any storage error. - - Args: - session: The profile session to use - state: The state to set - reason: A reason to add to the log - log_params: Additional parameters to log - log_override: Override configured logging regimen, print to stderr instead - """ - - if self._last_state == state: # already done - return - - self.state = state or V10CredentialExchange.STATE_ABANDONED - if reason: - self.error_msg = reason - - try: - await self.save( - session, - reason=reason, - log_params=log_params, - log_override=log_override, - ) - except StorageError: - LOGGER.exception("Error saving credential exchange error state") - - # Override - async def emit_event(self, session: ProfileSession, payload: Optional[Any] = None): - """Emit an event. - - Args: - session: The profile session to use - payload: The event payload - """ - - if not self.RECORD_TOPIC: - return - - if self.state: - topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}::{self.state}" - else: - topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}" - - if session.profile.settings.get("debug.webhooks"): - if not payload: - payload = self.serialize() - else: - payload = V10CredentialExchangeWebhook(**self.__dict__) - payload = payload.__dict__ - - await session.profile.notify(topic, payload) - - @property - def record_value(self) -> dict: - """Accessor for the JSON record value generated for this invitation.""" - return { - **{ - prop: getattr(self, prop) - for prop in ( - "connection_id", - "credential_request_metadata", - "error_msg", - "auto_offer", - "auto_issue", - "auto_remove", - "parent_thread_id", - "initiator", - "credential_definition_id", - "schema_id", - "credential_id", - "revoc_reg_id", - "revocation_id", - "role", - "state", - "trace", - ) - }, - **{ - prop: getattr(self, f"_{prop}").ser - for prop in ( - "credential_proposal_dict", - "credential_offer_dict", - "credential_offer", - "credential_request", - "raw_credential", - "credential", - ) - if getattr(self, prop) is not None - }, - } - - @classmethod - async def retrieve_by_connection_and_thread( - cls, - session: ProfileSession, - connection_id: Optional[str], - thread_id: str, - role: Optional[str] = None, - *, - for_update=False, - ) -> "V10CredentialExchange": - """Retrieve a credential exchange record by connection and thread ID.""" - cache_key = f"credential_exchange_ctidx::{connection_id}::{thread_id}::{role}" - record_id = await cls.get_cached_key(session, cache_key) - if record_id: - record = await cls.retrieve_by_id(session, record_id, for_update=for_update) - else: - post_filter = {} - if role: - post_filter["role"] = role - if connection_id: - post_filter["connection_id"] = connection_id - record = await cls.retrieve_by_tag_filter( - session, - {"thread_id": thread_id}, - post_filter, - for_update=for_update, - ) - await cls.set_cached_key(session, cache_key, record.credential_exchange_id) - return record - - def __eq__(self, other: Any) -> bool: - """Comparison between records.""" - return super().__eq__(other) - - -class V10CredentialExchangeSchema(BaseExchangeSchema): - """Schema to allow serialization/deserialization of credential exchange records.""" - - class Meta: - """V10CredentialExchangeSchema metadata.""" - - model_class = V10CredentialExchange - - credential_exchange_id = fields.Str( - required=False, - metadata={ - "description": "Credential exchange identifier", - "example": UUID4_EXAMPLE, - }, - ) - connection_id = fields.Str( - required=False, - metadata={"description": "Connection identifier", "example": UUID4_EXAMPLE}, - ) - thread_id = fields.Str( - required=False, - metadata={"description": "Thread identifier", "example": UUID4_EXAMPLE}, - ) - parent_thread_id = fields.Str( - required=False, - metadata={ - "description": "Parent thread identifier", - "example": UUID4_EXAMPLE, - }, - ) - initiator = fields.Str( - required=False, - validate=validate.OneOf(["self", "external"]), - metadata={ - "description": "Issue-credential exchange initiator: self or external", - "example": V10CredentialExchange.INITIATOR_SELF, - }, - ) - role = fields.Str( - required=False, - validate=validate.OneOf(["holder", "issuer"]), - metadata={ - "description": "Issue-credential exchange role: holder or issuer", - "example": V10CredentialExchange.ROLE_ISSUER, - }, - ) - state = fields.Str( - required=False, - metadata={ - "description": "Issue-credential exchange state", - "example": V10CredentialExchange.STATE_ACKED, - }, - ) - credential_definition_id = fields.Str( - required=False, - validate=INDY_CRED_DEF_ID_VALIDATE, - metadata={ - "description": "Credential definition identifier", - "example": INDY_CRED_DEF_ID_EXAMPLE, - }, - ) - schema_id = fields.Str( - required=False, - validate=INDY_SCHEMA_ID_VALIDATE, - metadata={ - "description": "Schema identifier", - "example": INDY_SCHEMA_ID_EXAMPLE, - }, - ) - credential_proposal_dict = fields.Nested( - CredentialProposalSchema(), - required=False, - metadata={"description": "Credential proposal message"}, - ) - credential_offer_dict = fields.Nested( - CredentialOfferSchema(), - required=False, - metadata={"description": "Credential offer message"}, - ) - credential_offer = fields.Nested( - IndyCredAbstractSchema(), - required=False, - metadata={"description": "(Indy) credential offer"}, - ) - credential_request = fields.Nested( - IndyCredRequestSchema(), - required=False, - metadata={"description": "(Indy) credential request"}, - ) - credential_request_metadata = fields.Dict( - required=False, metadata={"description": "(Indy) credential request metadata"} - ) - credential_id = fields.Str( - required=False, - metadata={"description": "Credential identifier", "example": UUID4_EXAMPLE}, - ) - raw_credential = fields.Nested( - IndyCredentialSchema(), - required=False, - metadata={ - "description": "Credential as received, prior to storage in holder wallet" - }, - ) - credential = fields.Nested( - IndyCredInfoSchema(), - required=False, - metadata={"description": "Credential as stored"}, - ) - auto_offer = fields.Bool( - required=False, - metadata={ - "description": "Holder choice to accept offer in this credential exchange", - "example": False, - }, - ) - auto_issue = fields.Bool( - required=False, - metadata={ - "description": ( - "Issuer choice to issue to request in this credential exchange" - ), - "example": False, - }, - ) - auto_remove = fields.Bool( - required=False, - dump_default=True, - metadata={ - "description": ( - "Issuer choice to remove this credential exchange record when complete" - ), - "example": False, - }, - ) - error_msg = fields.Str( - required=False, - metadata={ - "description": "Error message", - "example": "Credential definition identifier is not set in proposal", - }, - ) - revoc_reg_id = fields.Str( - required=False, metadata={"description": "Revocation registry identifier"} - ) - revocation_id = fields.Str( - required=False, - metadata={"description": "Credential identifier within revocation registry"}, - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/tests/__init__.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/models/tests/test_credential_exchange.py b/aries_cloudagent/protocols/issue_credential/v1_0/models/tests/test_credential_exchange.py deleted file mode 100644 index 5bf9ccc293..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/models/tests/test_credential_exchange.py +++ /dev/null @@ -1,87 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......core.in_memory import InMemoryProfile -from ...messages.credential_proposal import CredentialProposal -from ...messages.inner.credential_preview import CredAttrSpec, CredentialPreview -from .. import credential_exchange as test_module -from ..credential_exchange import V10CredentialExchange - -TEST_DID = "LjgpST2rjsoxYegQDRm7EL" -SCHEMA_NAME = "bc-reg" -SCHEMA_TXN = 12 -SCHEMA_ID = f"{TEST_DID}:2:{SCHEMA_NAME}:1.0" -SCHEMA = { - "ver": "1.0", - "id": SCHEMA_ID, - "name": SCHEMA_NAME, - "version": "1.0", - "attrNames": ["legalName", "jurisdictionId", "incorporationDate"], - "seqNo": SCHEMA_TXN, -} -CRED_DEF_ID = f"{TEST_DID}:3:CL:12:tag1" -CRED_PREVIEW = CredentialPreview( - attributes=( - CredAttrSpec.list_plain({"test": "123", "hello": "world"}) - + [CredAttrSpec(name="icon", value="cG90YXRv", mime_type="image/png")] - ) -) - - -class TestV10CredentialExchange(IsolatedAsyncioTestCase): - """Test de/serialization.""" - - async def test_serde(self): - """Test de/serialization.""" - - credential_proposal = CredentialProposal( - comment="Hello World", - credential_proposal=CRED_PREVIEW, - schema_id=SCHEMA_ID, - cred_def_id=CRED_DEF_ID, - ) - for proposal_arg in [credential_proposal, credential_proposal.serialize()]: - cx_rec = V10CredentialExchange( - credential_exchange_id="dummy", - connection_id="0000...", - thread_id="dummy-thid", - parent_thread_id="dummy-pthid", - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_PROPOSAL_RECEIVED, - credential_definition_id=CRED_DEF_ID, - schema_id=SCHEMA_ID, - credential_proposal_dict=proposal_arg, - credential_request_metadata=None, - credential_id="cred-id", - revoc_reg_id=None, - revocation_id=None, - auto_offer=False, - auto_issue=False, - auto_remove=True, - error_msg=None, - trace=False, - ) - assert isinstance(cx_rec.credential_proposal_dict, CredentialProposal) - ser = cx_rec.serialize() - deser = V10CredentialExchange.deserialize(ser) - assert isinstance(deser.credential_proposal_dict, CredentialProposal) - - async def test_save_error_state(self): - session = InMemoryProfile.test_session() - record = V10CredentialExchange(state=None) - assert record._last_state is None - await record.save_error_state(session) # cover short circuit - - record.state = V10CredentialExchange.STATE_PROPOSAL_RECEIVED - await record.save(session) - - with mock.patch.object( - record, "save", mock.CoroutineMock() - ) as mock_save, mock.patch.object( - test_module.LOGGER, "exception", mock.MagicMock() - ) as mock_log_exc: - mock_save.side_effect = test_module.StorageError() - await record.save_error_state(session, reason="test") - mock_log_exc.assert_called_once() diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/routes.py deleted file mode 100644 index 725ad02992..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/routes.py +++ /dev/null @@ -1,1510 +0,0 @@ -"""Credential exchange admin routes.""" - -from json.decoder import JSONDecodeError -from typing import Optional - -from aiohttp import web -from aiohttp_apispec import ( - docs, - match_info_schema, - querystring_schema, - request_schema, - response_schema, -) -from marshmallow import fields, validate - -from ....admin.decorators.auth import tenant_authentication -from ....admin.request_context import AdminRequestContext -from ....connections.models.conn_record import ConnRecord -from ....core.profile import Profile -from ....indy.holder import IndyHolderError -from ....indy.issuer import IndyIssuerError -from ....ledger.error import LedgerError -from ....messaging.credential_definitions.util import CRED_DEF_TAGS -from ....messaging.models.base import BaseModelError -from ....messaging.models.openapi import OpenAPISchema -from ....messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset -from ....messaging.valid import ( - INDY_CRED_DEF_ID_EXAMPLE, - INDY_CRED_DEF_ID_VALIDATE, - INDY_DID_EXAMPLE, - INDY_DID_VALIDATE, - INDY_SCHEMA_ID_EXAMPLE, - INDY_SCHEMA_ID_VALIDATE, - INDY_VERSION_EXAMPLE, - INDY_VERSION_VALIDATE, - UUID4_EXAMPLE, - UUID4_VALIDATE, -) -from ....storage.error import StorageError, StorageNotFoundError -from ....utils.tracing import AdminAPIMessageTracingSchema, get_timer, trace_event -from ....wallet.util import default_did_from_verkey -from ...out_of_band.v1_0.models.oob_record import OobRecord -from . import problem_report_for_record, report_problem -from .manager import CredentialManager, CredentialManagerError -from .message_types import SPEC_URI -from .messages.credential_problem_report import ProblemReportReason -from .messages.credential_proposal import CredentialProposal, CredentialProposalSchema -from .messages.inner.credential_preview import ( - CredentialPreview, - CredentialPreviewSchema, -) -from .models.credential_exchange import ( - V10CredentialExchange, - V10CredentialExchangeSchema, -) - - -class IssueCredentialModuleResponseSchema(OpenAPISchema): - """Response schema for Issue Credential Module.""" - - -class V10CredentialExchangeListQueryStringSchema(PaginatedQuerySchema): - """Parameters and validators for credential exchange list query.""" - - connection_id = fields.Str( - required=False, - metadata={"description": "Connection identifier", "example": UUID4_EXAMPLE}, - ) - thread_id = fields.Str( - required=False, - metadata={"description": "Thread identifier", "example": UUID4_EXAMPLE}, - ) - role = fields.Str( - required=False, - validate=validate.OneOf( - [ - getattr(V10CredentialExchange, m) - for m in vars(V10CredentialExchange) - if m.startswith("ROLE_") - ] - ), - metadata={"description": "Role assigned in credential exchange"}, - ) - state = fields.Str( - required=False, - validate=validate.OneOf( - [ - getattr(V10CredentialExchange, m) - for m in vars(V10CredentialExchange) - if m.startswith("STATE_") - ] - ), - metadata={"description": "Credential exchange state"}, - ) - - -class V10CredentialExchangeListResultSchema(OpenAPISchema): - """Result schema for Aries#0036 v1.0 credential exchange query.""" - - results = fields.List( - fields.Nested(V10CredentialExchangeSchema), - metadata={"description": "Aries#0036 v1.0 credential exchange records"}, - ) - - -class V10CredentialStoreRequestSchema(OpenAPISchema): - """Request schema for sending a credential store admin message.""" - - credential_id = fields.Str(required=False) - - -class V10CredentialCreateSchema(AdminAPIMessageTracingSchema): - """Base class for request schema for sending credential proposal admin message.""" - - cred_def_id = fields.Str( - required=False, - validate=INDY_CRED_DEF_ID_VALIDATE, - metadata={ - "description": "Credential definition identifier", - "example": INDY_CRED_DEF_ID_EXAMPLE, - }, - ) - schema_id = fields.Str( - required=False, - validate=INDY_SCHEMA_ID_VALIDATE, - metadata={ - "description": "Schema identifier", - "example": INDY_SCHEMA_ID_EXAMPLE, - }, - ) - schema_issuer_did = fields.Str( - required=False, - validate=INDY_DID_VALIDATE, - metadata={"description": "Schema issuer DID", "example": INDY_DID_EXAMPLE}, - ) - schema_name = fields.Str( - required=False, - metadata={"description": "Schema name", "example": "preferences"}, - ) - schema_version = fields.Str( - required=False, - validate=INDY_VERSION_VALIDATE, - metadata={"description": "Schema version", "example": INDY_VERSION_EXAMPLE}, - ) - issuer_did = fields.Str( - required=False, - validate=INDY_DID_VALIDATE, - metadata={"description": "Credential issuer DID", "example": INDY_DID_EXAMPLE}, - ) - auto_remove = fields.Bool( - required=False, - metadata={ - "description": ( - "Whether to remove the credential exchange record on completion" - " (overrides --preserve-exchange-records configuration setting)" - ) - }, - ) - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - credential_proposal = fields.Nested(CredentialPreviewSchema, required=True) - - -class V10CredentialProposalRequestSchemaBase(AdminAPIMessageTracingSchema): - """Base class for request schema for sending credential proposal admin message.""" - - connection_id = fields.Str( - required=True, - metadata={"description": "Connection identifier", "example": UUID4_EXAMPLE}, - ) - cred_def_id = fields.Str( - required=False, - validate=INDY_CRED_DEF_ID_VALIDATE, - metadata={ - "description": "Credential definition identifier", - "example": INDY_CRED_DEF_ID_EXAMPLE, - }, - ) - schema_id = fields.Str( - required=False, - validate=INDY_SCHEMA_ID_VALIDATE, - metadata={ - "description": "Schema identifier", - "example": INDY_SCHEMA_ID_EXAMPLE, - }, - ) - schema_issuer_did = fields.Str( - required=False, - validate=INDY_DID_VALIDATE, - metadata={"description": "Schema issuer DID", "example": INDY_DID_EXAMPLE}, - ) - schema_name = fields.Str( - required=False, - metadata={"description": "Schema name", "example": "preferences"}, - ) - schema_version = fields.Str( - required=False, - validate=INDY_VERSION_VALIDATE, - metadata={"description": "Schema version", "example": INDY_VERSION_EXAMPLE}, - ) - issuer_did = fields.Str( - required=False, - validate=INDY_DID_VALIDATE, - metadata={"description": "Credential issuer DID", "example": INDY_DID_EXAMPLE}, - ) - auto_remove = fields.Bool( - required=False, - metadata={ - "description": ( - "Whether to remove the credential exchange record on completion" - " (overrides --preserve-exchange-records configuration setting)" - ) - }, - ) - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - - -class V10CredentialProposalRequestOptSchema(V10CredentialProposalRequestSchemaBase): - """Request schema for sending credential proposal on optional proposal preview.""" - - credential_proposal = fields.Nested(CredentialPreviewSchema, required=False) - - -class V10CredentialProposalRequestMandSchema(V10CredentialProposalRequestSchemaBase): - """Request schema for sending credential proposal on mandatory proposal preview.""" - - credential_proposal = fields.Nested(CredentialPreviewSchema, required=True) - - -class V10CredentialBoundOfferRequestSchema(OpenAPISchema): - """Request schema for sending bound credential offer admin message.""" - - counter_proposal = fields.Nested( - CredentialProposalSchema, - required=False, - metadata={"description": "Optional counter-proposal"}, - ) - - -class V10CredentialFreeOfferRequestSchema(AdminAPIMessageTracingSchema): - """Request schema for sending free credential offer admin message.""" - - connection_id = fields.Str( - required=True, - metadata={"description": "Connection identifier", "example": UUID4_EXAMPLE}, - ) - cred_def_id = fields.Str( - required=True, - validate=INDY_CRED_DEF_ID_VALIDATE, - metadata={ - "description": "Credential definition identifier", - "example": INDY_CRED_DEF_ID_EXAMPLE, - }, - ) - auto_issue = fields.Bool( - required=False, - metadata={ - "description": ( - "Whether to respond automatically to credential requests, creating and" - " issuing requested credentials" - ) - }, - ) - auto_remove = fields.Bool( - required=False, - dump_default=True, - metadata={ - "description": ( - "Whether to remove the credential exchange record on completion" - " (overrides --preserve-exchange-records configuration setting)" - ) - }, - ) - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - credential_preview = fields.Nested(CredentialPreviewSchema, required=True) - - -class V10CredentialConnFreeOfferRequestSchema(AdminAPIMessageTracingSchema): - """Request schema for creating connection free credential offer.""" - - cred_def_id = fields.Str( - required=True, - validate=INDY_CRED_DEF_ID_VALIDATE, - metadata={ - "description": "Credential definition identifier", - "example": INDY_CRED_DEF_ID_EXAMPLE, - }, - ) - auto_issue = fields.Bool( - required=False, - metadata={ - "description": ( - "Whether to respond automatically to credential requests, creating and" - " issuing requested credentials" - ) - }, - ) - auto_remove = fields.Bool( - required=False, - dump_default=True, - metadata={ - "description": ( - "Whether to remove the credential exchange record on completion" - " (overrides --preserve-exchange-records configuration setting)" - ) - }, - ) - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - credential_preview = fields.Nested(CredentialPreviewSchema, required=True) - - -class V10CredentialIssueRequestSchema(OpenAPISchema): - """Request schema for sending credential issue admin message.""" - - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - - -class V10CredentialProblemReportRequestSchema(OpenAPISchema): - """Request schema for sending problem report.""" - - description = fields.Str(required=True) - - -class CredIdMatchInfoSchema(OpenAPISchema): - """Path parameters and validators for request taking credential id.""" - - credential_id = fields.Str( - required=True, - metadata={"description": "Credential identifier", "example": UUID4_EXAMPLE}, - ) - - -class CredExIdMatchInfoSchema(OpenAPISchema): - """Path parameters and validators for request taking credential exchange id.""" - - cred_ex_id = fields.Str( - required=True, - validate=UUID4_VALIDATE, - metadata={ - "description": "Credential exchange identifier", - "example": UUID4_EXAMPLE, - }, - ) - - -class V10CredentialExchangeAutoRemoveRequestSchema(OpenAPISchema): - """Request Schema for overriding default preserve exchange records setting.""" - - auto_remove = fields.Bool( - required=False, - dump_default=False, - metadata={ - "description": ( - "Whether to remove the credential exchange record on completion" - " (overrides --preserve-exchange-records configuration setting)" - ) - }, - ) - - -@docs( - tags=["issue-credential v1.0"], - summary="Fetch all credential exchange records", - deprecated=True, -) -@querystring_schema(V10CredentialExchangeListQueryStringSchema) -@response_schema(V10CredentialExchangeListResultSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_list(request: web.BaseRequest): - """Request handler for searching credential exchange records. - - Args: - request: aiohttp request object - - Returns: - The connection list response - - """ - context: AdminRequestContext = request["context"] - tag_filter = {} - if "thread_id" in request.query and request.query["thread_id"] != "": - tag_filter["thread_id"] = request.query["thread_id"] - post_filter = { - k: request.query[k] - for k in ("connection_id", "role", "state") - if request.query.get(k, "") != "" - } - - limit, offset = get_limit_offset(request) - - try: - async with context.profile.session() as session: - records = await V10CredentialExchange.query( - session=session, - tag_filter=tag_filter, - limit=limit, - offset=offset, - post_filter_positive=post_filter, - ) - results = [record.serialize() for record in records] - except (StorageError, BaseModelError) as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - return web.json_response({"results": results}) - - -@docs( - tags=["issue-credential v1.0"], - summary="Fetch a single credential exchange record", - deprecated=True, -) -@match_info_schema(CredExIdMatchInfoSchema()) -@response_schema(V10CredentialExchangeSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_retrieve(request: web.BaseRequest): - """Request handler for fetching single credential exchange record. - - Args: - request: aiohttp request object - - Returns: - The credential exchange record - - """ - context: AdminRequestContext = request["context"] - outbound_handler = request["outbound_message_router"] - - credential_exchange_id = request.match_info["cred_ex_id"] - cred_ex_record = None - try: - async with context.profile.session() as session: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - session, credential_exchange_id - ) - result = cred_ex_record.serialize() - except StorageNotFoundError as err: - # no such cred ex record: not protocol error, user fat-fingered id - raise web.HTTPNotFound(reason=err.roll_up) from err - except (BaseModelError, StorageError) as err: - # present but broken or hopeless: protocol error - await report_problem( - err, - ProblemReportReason.ISSUANCE_ABANDONED.value, - web.HTTPBadRequest, - cred_ex_record, - outbound_handler, - ) - - return web.json_response(result) - - -@docs( - tags=["issue-credential v1.0"], - summary=( - "Create a credential record without " - "sending (generally for use with Out-Of-Band)" - ), - deprecated=True, -) -@request_schema(V10CredentialCreateSchema()) -@response_schema(V10CredentialExchangeSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_create(request: web.BaseRequest): - """Request handler for creating a credential from attr values. - - The internal credential record will be created without the credential - being sent to any connection. This can be used in conjunction with - the `oob` protocols to bind messages to an out of band message. - - Args: - request: aiohttp request object - - Returns: - The credential exchange record - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - body = await request.json() - - comment = body.get("comment") - preview_spec = body.get("credential_proposal") - if not preview_spec: - raise web.HTTPBadRequest(reason="credential_proposal must be provided") - auto_remove = body.get( - "auto_remove", not profile.settings.get("preserve_exchange_records") - ) - trace_msg = body.get("trace") - - try: - preview = CredentialPreview.deserialize(preview_spec) - - credential_proposal = CredentialProposal( - comment=comment, - credential_proposal=preview, - **{t: body.get(t) for t in CRED_DEF_TAGS if body.get(t)}, - ) - credential_proposal.assign_trace_decorator( - context.settings, - trace_msg, - ) - - trace_event( - context.settings, - credential_proposal, - outcome="credential_exchange_create.START", - ) - - credential_manager = CredentialManager(context.profile) - - ( - credential_exchange_record, - credential_offer_message, - ) = await credential_manager.prepare_send( - None, - credential_proposal=credential_proposal, - auto_remove=auto_remove, - comment=comment, - ) - except (StorageError, BaseModelError) as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - trace_event( - context.settings, - credential_offer_message, - outcome="credential_exchange_create.END", - perf_counter=r_time, - ) - - return web.json_response(credential_exchange_record.serialize()) - - -@docs( - tags=["issue-credential v1.0"], - summary="Send holder a credential, automating entire flow", - deprecated=True, -) -@request_schema(V10CredentialProposalRequestMandSchema()) -@response_schema(V10CredentialExchangeSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_send(request: web.BaseRequest): - """Request handler for sending credential from issuer to holder from attr values. - - If both issuer and holder are configured for automatic responses, the operation - ultimately results in credential issue; otherwise, the result waits on the first - response not automated; the credential exchange record retains state regardless. - - Args: - request: aiohttp request object - - Returns: - The credential exchange record - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - body = await request.json() - - comment = body.get("comment") - connection_id = body.get("connection_id") - preview_spec = body.get("credential_proposal") - if not preview_spec: - raise web.HTTPBadRequest(reason="credential_proposal must be provided") - auto_remove = body.get( - "auto_remove", not profile.settings.get("preserve_exchange_records") - ) - trace_msg = body.get("trace") - - connection_record = None - cred_ex_record = None - try: - preview = CredentialPreview.deserialize(preview_spec) - async with profile.session() as session: - connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - if not connection_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") - - credential_proposal = CredentialProposal( - comment=comment, - credential_proposal=preview, - **{t: body.get(t) for t in CRED_DEF_TAGS if body.get(t)}, - ) - credential_proposal.assign_trace_decorator( - context.settings, - trace_msg, - ) - - trace_event( - context.settings, - credential_proposal, - outcome="credential_exchange_send.START", - ) - - credential_manager = CredentialManager(profile) - ( - cred_ex_record, - credential_offer_message, - ) = await credential_manager.prepare_send( - connection_id, - credential_proposal=credential_proposal, - auto_remove=auto_remove, - comment=comment, - ) - result = cred_ex_record.serialize() - - except (BaseModelError, CredentialManagerError, LedgerError, StorageError) as err: - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state(session, reason=err.roll_up) - await report_problem( - err, - ProblemReportReason.ISSUANCE_ABANDONED.value, - web.HTTPBadRequest, - cred_ex_record or connection_record, - outbound_handler, - ) - - await outbound_handler( - credential_offer_message, connection_id=cred_ex_record.connection_id - ) - - trace_event( - context.settings, - credential_offer_message, - outcome="credential_exchange_send.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["issue-credential v1.0"], - summary="Send issuer a credential proposal", - deprecated=True, -) -@request_schema(V10CredentialProposalRequestOptSchema()) -@response_schema(V10CredentialExchangeSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_send_proposal(request: web.BaseRequest): - """Request handler for sending credential proposal. - - Args: - request: aiohttp request object - - Returns: - The credential exchange record - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - body = await request.json() - - connection_id = body.get("connection_id") - comment = body.get("comment") - preview_spec = body.get("credential_proposal") - auto_remove = body.get( - "auto_remove", not profile.settings.get("preserve_exchange_records") - ) - trace_msg = body.get("trace") - - connection_record = None - cred_ex_record = None - try: - preview = CredentialPreview.deserialize(preview_spec) if preview_spec else None - async with profile.session() as session: - connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - if not connection_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") - - credential_manager = CredentialManager(profile) - cred_ex_record = await credential_manager.create_proposal( - connection_id, - comment=comment, - credential_preview=preview, - auto_remove=auto_remove, - trace=trace_msg, - **{t: body.get(t) for t in CRED_DEF_TAGS if body.get(t)}, - ) - - credential_proposal = cred_ex_record.credential_proposal_dict - result = cred_ex_record.serialize() - - except (BaseModelError, StorageError) as err: - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state(session, reason=err.roll_up) - # other party cannot yet receive a problem report about our failed protocol start - raise web.HTTPBadRequest(reason=err.roll_up) - - await outbound_handler( - credential_proposal, - connection_id=connection_id, - ) - - trace_event( - context.settings, - credential_proposal, - outcome="credential_exchange_send_proposal.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -async def _create_free_offer( - profile: Profile, - cred_def_id: str, - connection_id: Optional[str] = None, - auto_issue: bool = False, - auto_remove: bool = False, - preview_spec: Optional[dict] = None, - comment: Optional[str] = None, - trace_msg: Optional[bool] = None, -): - """Create a credential offer and related exchange record.""" - - credential_preview = CredentialPreview.deserialize(preview_spec) - credential_proposal = CredentialProposal( - comment=comment, - credential_proposal=credential_preview, - cred_def_id=cred_def_id, - ) - credential_proposal.assign_trace_decorator( - profile.settings, - trace_msg, - ) - credential_proposal_dict = credential_proposal.serialize() - - cred_ex_record = V10CredentialExchange( - connection_id=connection_id, - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - credential_definition_id=cred_def_id, - credential_proposal_dict=credential_proposal_dict, - auto_issue=auto_issue, - auto_remove=auto_remove, - trace=trace_msg, - ) - - credential_manager = CredentialManager(profile) - - (cred_ex_record, credential_offer_message) = await credential_manager.create_offer( - cred_ex_record, - counter_proposal=None, - comment=comment, - ) - - return (cred_ex_record, credential_offer_message) - - -@docs( - tags=["issue-credential v1.0"], - summary="Create a credential offer, independent of any proposal or connection", - deprecated=True, -) -@request_schema(V10CredentialConnFreeOfferRequestSchema()) -@response_schema(V10CredentialExchangeSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_create_free_offer(request: web.BaseRequest): - """Request handler for creating free credential offer. - - Unlike with `send-offer`, this credential exchange is not tied to a specific - connection. It must be dispatched out-of-band by the controller. - - Args: - request: aiohttp request object - - Returns: - The credential exchange record - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - body = await request.json() - - cred_def_id = body.get("cred_def_id") - if not cred_def_id: - raise web.HTTPBadRequest(reason="cred_def_id is required") - - auto_issue = body.get( - "auto_issue", context.settings.get("debug.auto_respond_credential_request") - ) - auto_remove = body.get( - "auto_remove", not profile.settings.get("preserve_exchange_records") - ) - comment = body.get("comment") - preview_spec = body.get("credential_preview") - if not preview_spec: - raise web.HTTPBadRequest(reason="Missing credential_preview") - - trace_msg = body.get("trace") - cred_ex_record = None - try: - (cred_ex_record, credential_offer_message) = await _create_free_offer( - profile=profile, - cred_def_id=cred_def_id, - auto_issue=auto_issue, - auto_remove=auto_remove, - preview_spec=preview_spec, - comment=comment, - trace_msg=trace_msg, - ) - result = cred_ex_record.serialize() - except ( - BaseModelError, - CredentialManagerError, - IndyIssuerError, - LedgerError, - StorageError, - ) as err: - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state(session, reason=err.roll_up) - raise web.HTTPBadRequest(reason=err.roll_up) - trace_event( - context.settings, - credential_offer_message, - outcome="credential_exchange_create_free_offer.END", - perf_counter=r_time, - ) - return web.json_response(result) - - -@docs( - tags=["issue-credential v1.0"], - summary="Send holder a credential offer, independent of any proposal", - deprecated=True, -) -@request_schema(V10CredentialFreeOfferRequestSchema()) -@response_schema(V10CredentialExchangeSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_send_free_offer(request: web.BaseRequest): - """Request handler for sending free credential offer. - - An issuer initiates a such a credential offer, free from any - holder-initiated corresponding credential proposal with preview. - - Args: - request: aiohttp request object - - Returns: - The credential exchange record - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - body = await request.json() - - connection_id = body.get("connection_id") - cred_def_id = body.get("cred_def_id") - if not cred_def_id: - raise web.HTTPBadRequest(reason="cred_def_id is required") - - auto_issue = body.get( - "auto_issue", context.settings.get("debug.auto_respond_credential_request") - ) - auto_remove = body.get( - "auto_remove", not profile.settings.get("preserve_exchange_records") - ) - comment = body.get("comment") - preview_spec = body.get("credential_preview") - if not preview_spec: - raise web.HTTPBadRequest(reason="Missing credential_preview") - trace_msg = body.get("trace") - - cred_ex_record = None - connection_record = None - try: - async with profile.session() as session: - connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - if not connection_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") - - cred_ex_record, credential_offer_message = await _create_free_offer( - profile=profile, - cred_def_id=cred_def_id, - connection_id=connection_id, - auto_issue=auto_issue, - auto_remove=auto_remove, - preview_spec=preview_spec, - comment=comment, - trace_msg=trace_msg, - ) - result = cred_ex_record.serialize() - - except ( - StorageNotFoundError, - BaseModelError, - CredentialManagerError, - LedgerError, - ) as err: - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state(session, reason=err.roll_up) - # other party cannot yet receive a problem report about our failed protocol start - raise web.HTTPBadRequest(reason=err.roll_up) - - await outbound_handler(credential_offer_message, connection_id=connection_id) - - trace_event( - context.settings, - credential_offer_message, - outcome="credential_exchange_send_free_offer.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["issue-credential v1.0"], - summary="Send holder a credential offer in reference to a proposal with preview", - deprecated=True, -) -@match_info_schema(CredExIdMatchInfoSchema()) -@request_schema(V10CredentialBoundOfferRequestSchema()) -@response_schema(V10CredentialExchangeSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_send_bound_offer(request: web.BaseRequest): - """Request handler for sending bound credential offer. - - A holder initiates this sequence with a credential proposal; this message - responds with an offer bound to the proposal. - - Args: - request: aiohttp request object - - Returns: - The credential exchange record - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - body = await request.json() if request.body_exists else {} - proposal_spec = body.get("counter_proposal") - - credential_exchange_id = request.match_info["cred_ex_id"] - cred_ex_record = None - connection_record = None - try: - async with profile.session() as session: - try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - session, credential_exchange_id - ) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - - if cred_ex_record.state != ( - V10CredentialExchange.STATE_PROPOSAL_RECEIVED - ): # check state here: manager call creates free offers too - raise CredentialManagerError( - f"Credential exchange {cred_ex_record.credential_exchange_id} " - f"in {cred_ex_record.state} state " - f"(must be {V10CredentialExchange.STATE_PROPOSAL_RECEIVED})" - ) - - connection_id = cred_ex_record.connection_id - connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - if not connection_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") - - credential_manager = CredentialManager(profile) - ( - cred_ex_record, - credential_offer_message, - ) = await credential_manager.create_offer( - cred_ex_record, - counter_proposal=( - CredentialProposal.deserialize(proposal_spec) if proposal_spec else None - ), - comment=None, - ) - - result = cred_ex_record.serialize() - - except ( - BaseModelError, - CredentialManagerError, - IndyIssuerError, - LedgerError, - StorageError, - ) as err: - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state(session, reason=err.roll_up) - await report_problem( - err, - ProblemReportReason.ISSUANCE_ABANDONED.value, - web.HTTPBadRequest, - cred_ex_record, - outbound_handler, - ) - - await outbound_handler(credential_offer_message, connection_id=connection_id) - - trace_event( - context.settings, - credential_offer_message, - outcome="credential_exchange_send_bound_offer.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["issue-credential v1.0"], - summary="Send issuer a credential request", - deprecated=True, -) -@match_info_schema(CredExIdMatchInfoSchema()) -@request_schema(V10CredentialExchangeAutoRemoveRequestSchema()) -@response_schema(V10CredentialExchangeSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_send_request(request: web.BaseRequest): - """Request handler for sending credential request. - - Args: - request: aiohttp request object - - Returns: - The credential exchange record - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - credential_exchange_id = request.match_info["cred_ex_id"] - - try: - body = await request.json() or {} - auto_remove = body.get( - "auto_remove", not profile.settings.get("preserve_exchange_records") - ) - except JSONDecodeError: - auto_remove = not profile.settings.get("preserve_exchange_records") - - cred_ex_record = None - connection_record = None - - async with profile.session() as session: - try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - session, credential_exchange_id - ) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - - # Fetch connection if exchange has record - connection_record = None - if cred_ex_record.connection_id: - try: - connection_record = await ConnRecord.retrieve_by_id( - session, cred_ex_record.connection_id - ) - except StorageNotFoundError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - if connection_record and not connection_record.is_ready: - raise web.HTTPForbidden( - reason=f"Connection {connection_record.connection_id} not ready" - ) - - if connection_record: - holder_did = connection_record.my_did - else: - # Need to get the holder DID from the out of band record - async with profile.session() as session: - oob_record = await OobRecord.retrieve_by_tag_filter( - session, - {"invi_msg_id": cred_ex_record.credential_offer_dict._thread.pthid}, - ) - # Transform recipient key into did - holder_did = default_did_from_verkey(oob_record.our_recipient_key) - - # assign the auto_remove flag from above... - cred_ex_record.auto_remove = auto_remove - - try: - credential_manager = CredentialManager(profile) - ( - cred_ex_record, - credential_request_message, - ) = await credential_manager.create_request(cred_ex_record, holder_did) - - result = cred_ex_record.serialize() - - except ( - BaseModelError, - CredentialManagerError, - IndyHolderError, - LedgerError, - StorageError, - ) as err: - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state(session, reason=err.roll_up) - await report_problem( - err, - ProblemReportReason.ISSUANCE_ABANDONED.value, - web.HTTPBadRequest, - cred_ex_record, - outbound_handler, - ) - - await outbound_handler( - credential_request_message, connection_id=cred_ex_record.connection_id - ) - - trace_event( - context.settings, - credential_request_message, - outcome="credential_exchange_send_request.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["issue-credential v1.0"], - summary="Send holder a credential", - deprecated=True, -) -@match_info_schema(CredExIdMatchInfoSchema()) -@request_schema(V10CredentialIssueRequestSchema()) -@response_schema(V10CredentialExchangeSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_issue(request: web.BaseRequest): - """Request handler for sending credential. - - Args: - request: aiohttp request object - - Returns: - The credential exchange record - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - body = await request.json() - comment = body.get("comment") - - credential_exchange_id = request.match_info["cred_ex_id"] - - cred_ex_record = None - connection_record = None - - async with profile.session() as session: - try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - session, credential_exchange_id - ) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - - # Fetch connection if exchange has record - connection_record = None - if cred_ex_record.connection_id: - try: - connection_record = await ConnRecord.retrieve_by_id( - session, cred_ex_record.connection_id - ) - except StorageNotFoundError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - if connection_record and not connection_record.is_ready: - raise web.HTTPForbidden( - reason=f"Connection {connection_record.connection_id} not ready" - ) - - try: - credential_manager = CredentialManager(profile) - ( - cred_ex_record, - credential_issue_message, - ) = await credential_manager.issue_credential(cred_ex_record, comment=comment) - - result = cred_ex_record.serialize() - - except ( - BaseModelError, - CredentialManagerError, - IndyIssuerError, - LedgerError, - StorageError, - ) as err: - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state(session, reason=err.roll_up) - await report_problem( - err, - ProblemReportReason.ISSUANCE_ABANDONED.value, - web.HTTPBadRequest, - cred_ex_record, - outbound_handler, - ) - - await outbound_handler( - credential_issue_message, connection_id=cred_ex_record.connection_id - ) - - trace_event( - context.settings, - credential_issue_message, - outcome="credential_exchange_issue.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["issue-credential v1.0"], - summary="Store a received credential", - deprecated=True, -) -@match_info_schema(CredExIdMatchInfoSchema()) -@request_schema(V10CredentialStoreRequestSchema()) -@response_schema(V10CredentialExchangeSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_store(request: web.BaseRequest): - """Request handler for storing credential. - - Args: - request: aiohttp request object - - Returns: - The credential exchange record - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - try: - body = await request.json() or {} - credential_id = body.get("credential_id") - except JSONDecodeError: - credential_id = None - - credential_exchange_id = request.match_info["cred_ex_id"] - - cred_ex_record = None - connection_record = None - - async with profile.session() as session: - try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - session, credential_exchange_id - ) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - - # Fetch connection if exchange has record - if cred_ex_record.connection_id: - try: - connection_record = await ConnRecord.retrieve_by_id( - session, cred_ex_record.connection_id - ) - except StorageNotFoundError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - if connection_record and not connection_record.is_ready: - raise web.HTTPForbidden( - reason=f"Connection {connection_record.connection_id} not ready" - ) - - try: - credential_manager = CredentialManager(profile) - cred_ex_record = await credential_manager.store_credential( - cred_ex_record, - credential_id, - ) - - except ( - CredentialManagerError, - IndyHolderError, - StorageError, - ) as err: # treat failure to store as mangled on receipt hence protocol error - if cred_ex_record: - async with profile.session() as session: - await cred_ex_record.save_error_state(session, reason=err.roll_up) - await report_problem( - err, - ProblemReportReason.ISSUANCE_ABANDONED.value, - web.HTTPBadRequest, - cred_ex_record, - outbound_handler, - ) - - try: # protocol owes an ack - ( - cred_ex_record, - credential_ack_message, - ) = await credential_manager.send_credential_ack(cred_ex_record) - result = cred_ex_record.serialize() # pick up state done - - except ( - BaseModelError, - CredentialManagerError, - StorageError, - ) as err: - # protocol finished OK: do not send problem report nor set record state error - raise web.HTTPBadRequest(reason=err.roll_up) from err - - trace_event( - context.settings, - credential_ack_message, - outcome="credential_exchange_store.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["issue-credential v1.0"], - summary="Send a problem report for credential exchange", - deprecated=True, -) -@match_info_schema(CredExIdMatchInfoSchema()) -@request_schema(V10CredentialProblemReportRequestSchema()) -@response_schema(IssueCredentialModuleResponseSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_problem_report(request: web.BaseRequest): - """Request handler for sending problem report. - - Args: - request: aiohttp request object - - """ - context: AdminRequestContext = request["context"] - outbound_handler = request["outbound_message_router"] - - credential_exchange_id = request.match_info["cred_ex_id"] - body = await request.json() - description = body["description"] - - try: - async with context.profile.session() as session: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - session, credential_exchange_id - ) - - if not cred_ex_record.connection_id: - raise web.HTTPBadRequest( - reason="No connection associated with credential exchange." - ) - report = problem_report_for_record(cred_ex_record, description) - await cred_ex_record.save_error_state( - session, - reason=f"created problem report: {description}", - ) - except StorageNotFoundError as err: # other party does not care about meta-problems - raise web.HTTPNotFound(reason=err.roll_up) from err - except StorageError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - await outbound_handler(report, connection_id=cred_ex_record.connection_id) - - return web.json_response({}) - - -@docs( - tags=["issue-credential v1.0"], - summary="Remove an existing credential exchange record", - deprecated=True, -) -@match_info_schema(CredExIdMatchInfoSchema()) -@response_schema(IssueCredentialModuleResponseSchema(), 200, description="") -@tenant_authentication -async def credential_exchange_remove(request: web.BaseRequest): - """Request handler for removing a credential exchange record. - - Args: - request: aiohttp request object - - """ - context: AdminRequestContext = request["context"] - - credential_exchange_id = request.match_info["cred_ex_id"] - cred_ex_record = None - try: - async with context.profile.session() as session: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - session, credential_exchange_id - ) - await cred_ex_record.delete_record(session) - except StorageNotFoundError as err: # not a protocol error - raise web.HTTPNotFound(reason=err.roll_up) from err - except StorageError as err: # not a protocol error - raise web.HTTPBadRequest(reason=err.roll_up) from err - - return web.json_response({}) - - -async def register(app: web.Application): - """Register routes.""" - - app.add_routes( - [ - web.get( - "/issue-credential/records", credential_exchange_list, allow_head=False - ), - web.post( - "/issue-credential/create-offer", credential_exchange_create_free_offer - ), - web.get( - "/issue-credential/records/{cred_ex_id}", - credential_exchange_retrieve, - allow_head=False, - ), - web.post("/issue-credential/create", credential_exchange_create), - web.post("/issue-credential/send", credential_exchange_send), - web.post( - "/issue-credential/send-proposal", credential_exchange_send_proposal - ), - web.post("/issue-credential/send-offer", credential_exchange_send_free_offer), - web.post( - "/issue-credential/records/{cred_ex_id}/send-offer", - credential_exchange_send_bound_offer, - ), - web.post( - "/issue-credential/records/{cred_ex_id}/send-request", - credential_exchange_send_request, - ), - web.post( - "/issue-credential/records/{cred_ex_id}/issue", - credential_exchange_issue, - ), - web.post( - "/issue-credential/records/{cred_ex_id}/store", - credential_exchange_store, - ), - web.post( - "/issue-credential/records/{cred_ex_id}/problem-report", - credential_exchange_problem_report, - ), - web.delete( - "/issue-credential/records/{cred_ex_id}", - credential_exchange_remove, - ), - ] - ) - - -def post_process_routes(app: web.Application): - """Amend swagger API.""" - - # Add top-level tags description - if "tags" not in app._state["swagger_dict"]: - app._state["swagger_dict"]["tags"] = [] - app._state["swagger_dict"]["tags"].append( - { - "name": "issue-credential v1.0", - "description": "Credential issue v1.0", - "externalDocs": {"description": "Specification", "url": SPEC_URI}, - } - ) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/__init__.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/__init__.py deleted file mode 100644 index 4d003024e0..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/__init__.py +++ /dev/null @@ -1,163 +0,0 @@ -"""Package-wide code and data.""" - -TEST_DID = "LjgpST2rjsoxYegQDRm7EL" -SCHEMA_NAME = "bc-reg" -SCHEMA_TXN = 12 -SCHEMA_ID = f"{TEST_DID}:2:{SCHEMA_NAME}:1.0" -SCHEMA = { - "ver": "1.0", - "id": SCHEMA_ID, - "name": SCHEMA_NAME, - "version": "1.0", - "attrNames": ["legalName", "jurisdictionId", "incorporationDate"], - "seqNo": SCHEMA_TXN, -} -CRED_DEF_ID = f"{TEST_DID}:3:CL:12:tag1" -CRED_DEF = { - "ver": "1.0", - "id": CRED_DEF_ID, - "schemaId": SCHEMA_TXN, - "type": "CL", - "tag": "tag1", - "value": { - "primary": { - "n": "...", - "s": "...", - "r": { - "master_secret": "...", - "legalName": "...", - "jurisdictionId": "...", - "incorporationDate": "...", - }, - "rctxt": "...", - "z": "...", - }, - "revocation": { - "g": "1 ...", - "g_dash": "1 ...", - "h": "1 ...", - "h0": "1 ...", - "h1": "1 ...", - "h2": "1 ...", - "htilde": "1 ...", - "h_cap": "1 ...", - "u": "1 ...", - "pk": "1 ...", - "y": "1 ...", - }, - }, -} -REV_REG_DEF_TYPE = "CL_ACCUM" -REV_REG_ID = f"{TEST_DID}:4:{CRED_DEF_ID}:{REV_REG_DEF_TYPE}:tag1" -TAILS_DIR = "/tmp/indy/revocation/tails_files" -TAILS_HASH = "8UW1Sz5cqoUnK9hqQk7nvtKK65t7Chu3ui866J23sFyJ" -TAILS_LOCAL = f"{TAILS_DIR}/{TAILS_HASH}" -REV_REG_DEF = { - "ver": "1.0", - "id": REV_REG_ID, - "revocDefType": "CL_ACCUM", - "tag": "tag1", - "credDefId": CRED_DEF_ID, - "value": { - "issuanceType": "ISSUANCE_ON_DEMAND", - "maxCredNum": 5, - "publicKeys": {"accumKey": {"z": "1 ..."}}, - "tailsHash": TAILS_HASH, - "tailsLocation": TAILS_LOCAL, - }, -} -INDY_OFFER = { - "cred_def_id": CRED_DEF_ID, - "schema_id": SCHEMA_ID, - "nonce": "1234567890", - "key_correctness_proof": { - "c": "565827556", - "xz_cap": "287165348434097", - "xr_cap": [ - [ - "remainder", - "24717", - ], - [ - "number", - "133504566766407", - ], - [ - "master_secret", - "10339928600136745", - ], - ], - }, -} -INDY_CRED_REQ = { - "prover_did": TEST_DID, - "cred_def_id": CRED_DEF_ID, - "blinded_ms": { - "u": "12345", - "ur": "1 123467890ABCDEF", - "hidden_attributes": ["master_secret"], - "committed_attributes": {}, - }, - "blinded_ms_correctness_proof": { - "c": "77777", - "v_dash_cap": "12345678901234567890", - "m_caps": {"master_secret": "271283714"}, - "r_caps": {}, - }, - "nonce": "9876543210", -} -INDY_CRED = { - "schema_id": SCHEMA_ID, - "cred_def_id": CRED_DEF_ID, - "rev_reg_id": REV_REG_ID, - "values": { - "legalName": { - "raw": "The Original House of Pies", - "encoded": "108156129846915621348916581250742315326283968964", - }, - "busId": {"raw": "11155555", "encoded": "11155555"}, - "jurisdictionId": {"raw": "1", "encoded": "1"}, - "incorporationDate": { - "raw": "2021-01-01", - "encoded": "121381685682968329568231", - }, - "pic": {"raw": "cG90YXRv", "encoded": "125362825623562385689562"}, - }, - "signature": { - "p_credential": { - "m_2": "13683295623862356", - "a": "1925723185621385238953", - "e": "253516862326", - "v": "26890295622385628356813632", - }, - "r_credential": { - "sigma": "1 00F81D", - "c": "158698926BD09866E", - "vr_prime_prime": "105682396DDF1A", - "witness_signature": {"sigma_i": "1 ...", "u_i": "1 ...", "g_i": "1 ..."}, - "g_i": "1 ...", - "i": 1, - "m2": "862186285926592362384FA97FF3A4AB", - }, - }, - "signature_correctness_proof": { - "se": "10582965928638296868123", - "c": "2816389562839651", - }, - "rev_reg": {"accum": "21 ..."}, - "witness": {"omega": "21 ..."}, -} -INDY_CRED_INFO = { - "referent": "reft", - "attrs": { - "legalName": "The Original House of Pies", - "busId": "11155555", - "jurisdictionId": "1", - "incorporationDate": "2021-01-01", - "pic": "cG90YXRv", - }, - "schema_id": SCHEMA_ID, - "cred_def_id": CRED_DEF_ID, - "rev_reg_id": REV_REG_ID, - "cred_rev_id": "1", -} diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py deleted file mode 100644 index ca1df030ee..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_manager.py +++ /dev/null @@ -1,1646 +0,0 @@ -import json -from copy import deepcopy -from time import time -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from .....cache.base import BaseCache -from .....cache.in_memory import InMemoryCache -from .....core.in_memory import InMemoryProfile -from .....indy.holder import IndyHolder -from .....indy.issuer import IndyIssuer -from .....ledger.base import BaseLedger -from .....ledger.multiple_ledger.ledger_requests_executor import ( - IndyLedgerRequestsExecutor, -) -from .....messaging.credential_definitions.util import CRED_DEF_SENT_RECORD_TYPE -from .....messaging.decorators.thread_decorator import ThreadDecorator -from .....messaging.responder import BaseResponder, MockResponder -from .....multitenant.base import BaseMultitenantManager -from .....multitenant.manager import MultitenantManager -from .....storage.base import StorageRecord -from .....storage.error import StorageNotFoundError -from .. import manager as test_module -from ..manager import CredentialManager, CredentialManagerError -from ..messages.credential_ack import CredentialAck -from ..messages.credential_issue import CredentialIssue -from ..messages.credential_offer import CredentialOffer -from ..messages.credential_problem_report import CredentialProblemReport -from ..messages.credential_proposal import CredentialProposal -from ..messages.credential_request import CredentialRequest -from ..messages.inner.credential_preview import CredAttrSpec, CredentialPreview -from ..models.credential_exchange import V10CredentialExchange -from . import ( - CRED_DEF, - CRED_DEF_ID, - INDY_CRED, - INDY_CRED_INFO, - INDY_CRED_REQ, - INDY_OFFER, - REV_REG_DEF, - REV_REG_ID, - SCHEMA, - SCHEMA_ID, - TEST_DID, -) - - -class TestCredentialManager(IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.session = InMemoryProfile.test_session() - self.profile = self.session.profile - self.context = self.profile.context - setattr(self.profile, "session", mock.MagicMock(return_value=self.session)) - setattr(self.profile, "transaction", mock.MagicMock(return_value=self.session)) - - Ledger = mock.MagicMock() - self.ledger = Ledger() - self.ledger.get_schema = mock.CoroutineMock(return_value=SCHEMA) - self.ledger.get_credential_definition = mock.CoroutineMock(return_value=CRED_DEF) - self.ledger.get_revoc_reg_def = mock.CoroutineMock(return_value=REV_REG_DEF) - self.ledger.__aenter__ = mock.CoroutineMock(return_value=self.ledger) - self.ledger.credential_definition_id2schema_id = mock.CoroutineMock( - return_value=SCHEMA_ID - ) - self.context.injector.bind_instance(BaseLedger, self.ledger) - self.context.injector.bind_instance( - IndyLedgerRequestsExecutor, - mock.MagicMock( - get_ledger_for_identifier=mock.CoroutineMock( - return_value=(None, self.ledger) - ) - ), - ) - self.manager = CredentialManager(self.profile) - assert self.manager.profile - - async def test_record_eq(self): - same = [ - V10CredentialExchange( - credential_exchange_id="dummy-0", - thread_id="thread-0", - credential_definition_id=CRED_DEF_ID, - role=V10CredentialExchange.ROLE_ISSUER, - ) - ] * 2 - diff = [ - V10CredentialExchange( - credential_exchange_id="dummy-1", - credential_definition_id=CRED_DEF_ID, - role=V10CredentialExchange.ROLE_ISSUER, - ), - V10CredentialExchange( - credential_exchange_id="dummy-0", - thread_id="thread-1", - credential_definition_id=CRED_DEF_ID, - role=V10CredentialExchange.ROLE_ISSUER, - ), - V10CredentialExchange( - credential_exchange_id="dummy-1", - thread_id="thread-0", - credential_definition_id=f"{CRED_DEF_ID}_distinct_tag", - role=V10CredentialExchange.ROLE_ISSUER, - ), - ] - - for i in range(len(same) - 1): - for j in range(i, len(same)): - assert same[i] == same[j] - - for i in range(len(diff) - 1): - for j in range(i, len(diff)): - assert diff[i] == diff[j] if i == j else diff[i] != diff[j] - - async def test_prepare_send(self): - connection_id = "test_conn_id" - preview = CredentialPreview( - attributes=( - CredAttrSpec(name="legalName", value="value"), - CredAttrSpec(name="jurisdictionId", value="value"), - CredAttrSpec(name="incorporationDate", value="value"), - ) - ) - proposal = CredentialProposal( - credential_proposal=preview, cred_def_id=CRED_DEF_ID, schema_id=SCHEMA_ID - ) - with mock.patch.object( - self.manager, "create_offer", autospec=True - ) as create_offer: - create_offer.return_value = (mock.MagicMock(), mock.MagicMock()) - ret_exchange, ret_cred_offer = await self.manager.prepare_send( - connection_id, proposal - ) - create_offer.assert_called_once() - assert ret_exchange is create_offer.return_value[0] - arg_exchange = create_offer.call_args[1]["cred_ex_record"] - assert arg_exchange.auto_issue - assert arg_exchange.connection_id == connection_id - assert arg_exchange.schema_id is None - assert arg_exchange.credential_definition_id is None - assert arg_exchange.role == V10CredentialExchange.ROLE_ISSUER - assert arg_exchange.credential_proposal_dict == proposal - - async def test_create_proposal(self): - connection_id = "test_conn_id" - comment = "comment" - preview = CredentialPreview( - attributes=( - CredAttrSpec(name="legalName", value="value"), - CredAttrSpec(name="jurisdictionId", value="value"), - CredAttrSpec(name="incorporationDate", value="value"), - ) - ) - - self.ledger.credential_definition_id2schema_id = mock.CoroutineMock( - return_value=SCHEMA_ID - ) - - with mock.patch.object(V10CredentialExchange, "save", autospec=True) as save_ex: - exchange: V10CredentialExchange = await self.manager.create_proposal( - connection_id, - auto_offer=True, - comment=comment, - credential_preview=preview, - cred_def_id=CRED_DEF_ID, - ) - save_ex.assert_called_once() - - await self.manager.create_proposal( - connection_id, - auto_offer=True, - comment=comment, - credential_preview=preview, - cred_def_id=None, - ) # OK to leave underspecified until offer - - proposal = exchange.credential_proposal_dict - - assert exchange.auto_offer - assert exchange.connection_id == connection_id - assert not exchange.credential_definition_id # leave underspecified until offer - assert not exchange.schema_id # leave underspecified until offer - assert exchange.thread_id == proposal._thread_id - assert exchange.role == exchange.ROLE_HOLDER - assert exchange.state == V10CredentialExchange.STATE_PROPOSAL_SENT - - async def test_create_proposal_no_preview(self): - connection_id = "test_conn_id" - comment = "comment" - - self.ledger.credential_definition_id2schema_id = mock.CoroutineMock( - return_value=SCHEMA_ID - ) - - with mock.patch.object(V10CredentialExchange, "save", autospec=True) as save_ex: - exchange: V10CredentialExchange = await self.manager.create_proposal( - connection_id, - auto_offer=True, - comment=comment, - credential_preview=None, - cred_def_id=CRED_DEF_ID, - ) - save_ex.assert_called_once() - - proposal = exchange.credential_proposal_dict - - assert exchange.auto_offer - assert exchange.connection_id == connection_id - assert not exchange.credential_definition_id # leave underspecified until offer - assert not exchange.schema_id # leave underspecified until offer - assert exchange.thread_id == proposal._thread_id - assert exchange.role == exchange.ROLE_HOLDER - assert exchange.state == V10CredentialExchange.STATE_PROPOSAL_SENT - - async def test_receive_proposal(self): - connection_id = "test_conn_id" - comment = "comment" - - preview = CredentialPreview( - attributes=( - CredAttrSpec(name="legalName", value="value"), - CredAttrSpec(name="jurisdictionId", value="value"), - CredAttrSpec(name="incorporationDate", value="value"), - ) - ) - - with mock.patch.object(V10CredentialExchange, "save", autospec=True) as save_ex: - proposal = CredentialProposal( - credential_proposal=preview, cred_def_id=CRED_DEF_ID, schema_id=None - ) - - exchange = await self.manager.receive_proposal(proposal, connection_id) - save_ex.assert_called_once() - - assert exchange.connection_id == connection_id - assert exchange.credential_definition_id is None - assert exchange.role == V10CredentialExchange.ROLE_ISSUER - assert exchange.state == V10CredentialExchange.STATE_PROPOSAL_RECEIVED - assert exchange.schema_id is None - assert exchange.thread_id == proposal._thread_id - - ret_proposal: CredentialProposal = exchange.credential_proposal_dict - attrs = ret_proposal.credential_proposal.attributes - assert attrs == preview.attributes - - self.context.message = CredentialProposal( - credential_proposal=preview, cred_def_id=None, schema_id=None - ) - await self.manager.receive_proposal( - proposal, connection_id - ) # OK to leave open until offer - - async def test_create_free_offer(self): - connection_id = "test_conn_id" - comment = "comment" - schema_id_parts = SCHEMA_ID.split(":") - - preview = CredentialPreview( - attributes=( - CredAttrSpec(name="legalName", value="value"), - CredAttrSpec(name="jurisdictionId", value="value"), - CredAttrSpec(name="incorporationDate", value="value"), - ) - ) - proposal = CredentialProposal( - credential_proposal=preview, cred_def_id=CRED_DEF_ID, schema_id=None - ) - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - credential_definition_id=CRED_DEF_ID, - role=V10CredentialExchange.ROLE_ISSUER, - credential_proposal_dict=proposal.serialize(), - new_with_id=True, - ) - await stored_exchange.save(self.session) - - with mock.patch.object(V10CredentialExchange, "save", autospec=True) as save_ex: - self.cache = InMemoryCache() - self.context.injector.bind_instance(BaseCache, self.cache) - - issuer = mock.MagicMock(IndyIssuer, autospec=True) - issuer.create_credential_offer = mock.CoroutineMock( - return_value=json.dumps(INDY_OFFER) - ) - self.context.injector.bind_instance(IndyIssuer, issuer) - - cred_def_record = StorageRecord( - CRED_DEF_SENT_RECORD_TYPE, - CRED_DEF_ID, - { - "schema_id": SCHEMA_ID, - "schema_issuer_did": schema_id_parts[0], - "schema_name": schema_id_parts[-2], - "schema_version": schema_id_parts[-1], - "issuer_did": TEST_DID, - "cred_def_id": CRED_DEF_ID, - "epoch": str(int(time())), - }, - ) - await self.session.storage.add_record(cred_def_record) - - (ret_exchange, ret_offer) = await self.manager.create_offer( - cred_ex_record=stored_exchange, - counter_proposal=None, - comment=comment, - ) - assert ret_exchange is stored_exchange - save_ex.assert_called_once() - - issuer.create_credential_offer.assert_called_once_with(CRED_DEF_ID) - - assert ( - stored_exchange.credential_exchange_id == ret_exchange._id - ) # cover property - assert stored_exchange.thread_id == ret_offer._thread_id - assert stored_exchange.credential_definition_id == CRED_DEF_ID - assert stored_exchange.role == V10CredentialExchange.ROLE_ISSUER - assert stored_exchange.schema_id == SCHEMA_ID - assert stored_exchange.state == V10CredentialExchange.STATE_OFFER_SENT - assert stored_exchange._credential_offer.ser == INDY_OFFER - - (ret_exchange, ret_offer) = await self.manager.create_offer( - cred_ex_record=stored_exchange, - counter_proposal=None, - comment=comment, - ) # once more to cover case where offer is available in cache - - async def test_create_free_offer_attr_mismatch(self): - connection_id = "test_conn_id" - comment = "comment" - schema_id_parts = SCHEMA_ID.split(":") - - preview = CredentialPreview( - attributes=( - CredAttrSpec(name="legal name", value="value"), - CredAttrSpec(name="jurisdiction id", value="value"), - CredAttrSpec(name="incorporation date", value="value"), - ) - ) - proposal = CredentialProposal( - credential_proposal=preview, cred_def_id=CRED_DEF_ID, schema_id=None - ) - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - credential_definition_id=CRED_DEF_ID, - role=V10CredentialExchange.ROLE_ISSUER, - credential_proposal_dict=proposal.serialize(), - new_with_id=True, - ) - self.context.injector.bind_instance( - BaseMultitenantManager, - mock.MagicMock(MultitenantManager, autospec=True), - ) - await stored_exchange.save(self.session) - - with mock.patch.object(V10CredentialExchange, "save", autospec=True) as save_ex: - self.cache = InMemoryCache() - self.context.injector.bind_instance(BaseCache, self.cache) - - issuer = mock.MagicMock(IndyIssuer, autospec=True) - issuer.create_credential_offer = mock.CoroutineMock( - return_value=json.dumps(INDY_OFFER) - ) - self.context.injector.bind_instance(IndyIssuer, issuer) - - cred_def_record = StorageRecord( - CRED_DEF_SENT_RECORD_TYPE, - CRED_DEF_ID, - { - "schema_id": SCHEMA_ID, - "schema_issuer_did": schema_id_parts[0], - "schema_name": schema_id_parts[-2], - "schema_version": schema_id_parts[-1], - "issuer_did": TEST_DID, - "cred_def_id": CRED_DEF_ID, - "epoch": str(int(time())), - }, - ) - await self.session.storage.add_record(cred_def_record) - - with self.assertRaises(CredentialManagerError): - await self.manager.create_offer( - cred_ex_record=stored_exchange, - counter_proposal=None, - comment=comment, - ) - - async def test_create_bound_offer(self): - TEST_DID = "LjgpST2rjsoxYegQDRm7EL" - schema_id_parts = SCHEMA_ID.split(":") - connection_id = "test_conn_id" - comment = "comment" - - preview = CredentialPreview( - attributes=( - CredAttrSpec(name="legalName", value="value"), - CredAttrSpec(name="jurisdictionId", value="value"), - CredAttrSpec(name="incorporationDate", value="value"), - ) - ) - proposal = CredentialProposal(credential_proposal=preview) - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - credential_proposal_dict=proposal.serialize(), - role=V10CredentialExchange.ROLE_ISSUER, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10CredentialExchange, "get_cached_key", autospec=True - ) as get_cached_key, mock.patch.object( - V10CredentialExchange, "set_cached_key", autospec=True - ) as set_cached_key: - get_cached_key.return_value = None - issuer = mock.MagicMock(IndyIssuer, autospec=True) - issuer.create_credential_offer = mock.CoroutineMock( - return_value=json.dumps(INDY_OFFER) - ) - self.context.injector.bind_instance(IndyIssuer, issuer) - - cred_def_record = StorageRecord( - CRED_DEF_SENT_RECORD_TYPE, - CRED_DEF_ID, - { - "schema_id": SCHEMA_ID, - "schema_issuer_did": schema_id_parts[0], - "schema_name": schema_id_parts[-2], - "schema_version": schema_id_parts[-1], - "issuer_did": TEST_DID, - "cred_def_id": CRED_DEF_ID, - "epoch": str(int(time())), - }, - ) - await self.session.storage.add_record(cred_def_record) - - (ret_exchange, ret_offer) = await self.manager.create_offer( - cred_ex_record=stored_exchange, - counter_proposal=None, - comment=comment, - ) - assert ret_exchange is stored_exchange - save_ex.assert_called_once() - - issuer.create_credential_offer.assert_called_once_with(CRED_DEF_ID) - - assert stored_exchange.thread_id == ret_offer._thread_id - assert stored_exchange.schema_id == SCHEMA_ID - assert stored_exchange.credential_definition_id == CRED_DEF_ID - assert stored_exchange.role == V10CredentialExchange.ROLE_ISSUER - assert stored_exchange.state == V10CredentialExchange.STATE_OFFER_SENT - assert stored_exchange._credential_offer.ser == INDY_OFFER - - # additionally check that credential preview was passed through - assert ret_offer.credential_preview.attributes == preview.attributes - - async def test_create_bound_offer_no_cred_def(self): - TEST_DID = "LjgpST2rjsoxYegQDRm7EL" - schema_id_parts = SCHEMA_ID.split(":") - connection_id = "test_conn_id" - comment = "comment" - - preview = CredentialPreview( - attributes=( - CredAttrSpec(name="legalName", value="value"), - CredAttrSpec(name="jurisdictionId", value="value"), - CredAttrSpec(name="incorporationDate", value="value"), - ) - ) - proposal = CredentialProposal(credential_proposal=preview) - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - credential_proposal_dict=proposal.serialize(), - role=V10CredentialExchange.ROLE_ISSUER, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10CredentialExchange, "get_cached_key", autospec=True - ) as get_cached_key, mock.patch.object( - V10CredentialExchange, "set_cached_key", autospec=True - ) as set_cached_key: - get_cached_key.return_value = None - issuer = mock.MagicMock() - issuer.create_credential_offer = mock.CoroutineMock(return_value=INDY_OFFER) - self.context.injector.bind_instance(IndyIssuer, issuer) - - with self.assertRaises(CredentialManagerError): - await self.manager.create_offer( - cred_ex_record=stored_exchange, - counter_proposal=None, - comment=comment, - ) - - async def test_receive_offer_proposed(self): - connection_id = "test_conn_id" - thread_id = "thread-id" - - preview = CredentialPreview( - attributes=( - CredAttrSpec(name="legalName", value="value"), - CredAttrSpec(name="jurisdictionId", value="value"), - CredAttrSpec(name="incorporationDate", value="value"), - ) - ) - proposal = CredentialProposal(credential_proposal=preview) - - offer = CredentialOffer( - credential_preview=preview, - offers_attach=[CredentialOffer.wrap_indy_offer(INDY_OFFER)], - ) - offer.assign_thread_id(thread_id) - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_proposal_dict=proposal.serialize(), - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_HOLDER, - state=V10CredentialExchange.STATE_PROPOSAL_SENT, - schema_id=SCHEMA_ID, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10CredentialExchange, - "retrieve_by_connection_and_thread", - mock.CoroutineMock(return_value=stored_exchange), - ) as retrieve_ex: - exchange = await self.manager.receive_offer(offer, connection_id) - - assert exchange.connection_id == connection_id - assert exchange.credential_definition_id == CRED_DEF_ID - assert exchange.schema_id == SCHEMA_ID - assert exchange.thread_id == offer._thread_id - assert exchange.role == V10CredentialExchange.ROLE_HOLDER - assert exchange.state == V10CredentialExchange.STATE_OFFER_RECEIVED - assert exchange._credential_offer.ser == INDY_OFFER - assert exchange.credential_offer_dict == offer - - proposal = exchange.credential_proposal_dict - assert proposal.credential_proposal.attributes == preview.attributes - - async def test_receive_free_offer(self): - connection_id = "test_conn_id" - preview = CredentialPreview( - attributes=( - CredAttrSpec(name="legalName", value="value"), - CredAttrSpec(name="jurisdictionId", value="value"), - CredAttrSpec(name="incorporationDate", value="value"), - ) - ) - - offer = CredentialOffer( - credential_preview=preview, - offers_attach=[CredentialOffer.wrap_indy_offer(INDY_OFFER)], - ) - self.context.message = offer - self.context.connection_record = mock.MagicMock() - self.context.connection_record.connection_id = connection_id - - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10CredentialExchange, - "retrieve_by_connection_and_thread", - mock.CoroutineMock(side_effect=StorageNotFoundError), - ) as retrieve_ex: - exchange = await self.manager.receive_offer(offer, connection_id) - - assert exchange.connection_id == connection_id - assert exchange.credential_definition_id == CRED_DEF_ID - assert exchange.schema_id == SCHEMA_ID - assert exchange.thread_id == offer._thread_id - assert exchange.role == V10CredentialExchange.ROLE_HOLDER - assert exchange.state == V10CredentialExchange.STATE_OFFER_RECEIVED - assert exchange._credential_offer.ser == INDY_OFFER - assert exchange.credential_proposal_dict - assert exchange.credential_offer_dict == offer - - async def test_create_request(self): - connection_id = "test_conn_id" - thread_id = "thread-id" - holder_did = "did" - - credential_offer_dict = CredentialOffer( - "thread-id", - ) - credential_offer_dict._thread = ThreadDecorator(pthid="some-pthid") - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_offer=INDY_OFFER, - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_HOLDER, - state=V10CredentialExchange.STATE_OFFER_RECEIVED, - credential_offer_dict=credential_offer_dict, - schema_id=SCHEMA_ID, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - self.cache = InMemoryCache() - self.context.injector.bind_instance(BaseCache, self.cache) - - with mock.patch.object(V10CredentialExchange, "save", autospec=True) as save_ex: - cred_def = {"cred": "def"} - self.ledger.get_credential_definition = mock.CoroutineMock( - return_value=cred_def - ) - - cred_req_meta = {} - holder = mock.MagicMock() - holder.create_credential_request = mock.CoroutineMock( - return_value=(json.dumps(INDY_CRED_REQ), json.dumps(cred_req_meta)) - ) - self.context.injector.bind_instance(IndyHolder, holder) - - ret_exchange, ret_request = await self.manager.create_request( - stored_exchange, holder_did - ) - - holder.create_credential_request.assert_called_once_with( - INDY_OFFER, cred_def, holder_did - ) - - assert ret_request.indy_cred_req() == INDY_CRED_REQ - assert ret_request._thread_id == thread_id - - assert ret_exchange.state == V10CredentialExchange.STATE_REQUEST_SENT - - # cover case with request in cache - stored_exchange.credential_request = None - stored_exchange.state = V10CredentialExchange.STATE_OFFER_RECEIVED - await self.manager.create_request(stored_exchange, holder_did) - - # cover case with existing cred req - ( - ret_existing_exchange, - ret_existing_request, - ) = await self.manager.create_request(ret_exchange, holder_did) - assert ret_existing_exchange == ret_exchange - assert ret_existing_request._thread_id == thread_id - assert ret_existing_request._thread.pthid == "some-pthid" - - async def test_create_request_no_cache(self): - connection_id = "test_conn_id" - thread_id = "thread-id" - holder_did = "did" - - credential_offer_dict = CredentialOffer( - "thread-id", - ) - credential_offer_dict._thread = ThreadDecorator(pthid="some-pthid") - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_offer=INDY_OFFER, - credential_offer_dict=credential_offer_dict, - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_HOLDER, - state=V10CredentialExchange.STATE_OFFER_RECEIVED, - schema_id=SCHEMA_ID, - thread_id=thread_id, - new_with_id=True, - ) - self.context.injector.bind_instance( - BaseMultitenantManager, - mock.MagicMock(MultitenantManager, autospec=True), - ) - await stored_exchange.save(self.session) - - with mock.patch.object(V10CredentialExchange, "save", autospec=True) as save_ex: - cred_def = {"cred": "def"} - self.ledger.get_credential_definition = mock.CoroutineMock( - return_value=cred_def - ) - - cred_req_meta = {} - holder = mock.MagicMock() - holder.create_credential_request = mock.CoroutineMock( - return_value=(json.dumps(INDY_CRED_REQ), json.dumps(cred_req_meta)) - ) - self.context.injector.bind_instance(IndyHolder, holder) - - ret_exchange, ret_request = await self.manager.create_request( - stored_exchange, holder_did - ) - - holder.create_credential_request.assert_called_once_with( - INDY_OFFER, cred_def, holder_did - ) - - assert ret_request.indy_cred_req() == INDY_CRED_REQ - assert ret_request._thread_id == thread_id - assert ret_request._thread.pthid == "some-pthid" - - assert ret_exchange.state == V10CredentialExchange.STATE_REQUEST_SENT - - async def test_create_request_bad_state(self): - connection_id = "test_conn_id" - thread_id = "thread-id" - holder_did = "did" - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_offer=INDY_OFFER, - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_HOLDER, - state=V10CredentialExchange.STATE_PROPOSAL_SENT, - schema_id=SCHEMA_ID, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - with self.assertRaises(CredentialManagerError): - await self.manager.create_request(stored_exchange, holder_did) - - async def test_receive_request(self): - mock_conn = mock.MagicMock(connection_id="test_conn_id") - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=mock_conn.connection_id, - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_OFFER_SENT, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - request = CredentialRequest( - requests_attach=[CredentialRequest.wrap_indy_cred_req(INDY_CRED_REQ)] - ) - - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10CredentialExchange, - "retrieve_by_connection_and_thread", - mock.CoroutineMock(return_value=stored_exchange), - ) as retrieve_ex: - exchange = await self.manager.receive_request(request, mock_conn, None) - - retrieve_ex.assert_called_once_with( - self.session, - "test_conn_id", - request._thread_id, - role=V10CredentialExchange.ROLE_ISSUER, - for_update=True, - ) - save_ex.assert_called_once() - - assert exchange.state == V10CredentialExchange.STATE_REQUEST_RECEIVED - assert exchange._credential_request.ser == INDY_CRED_REQ - - async def test_receive_request_no_connection_cred_request(self): - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_OFFER_SENT, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - request = CredentialRequest( - requests_attach=[CredentialRequest.wrap_indy_cred_req(INDY_CRED_REQ)] - ) - - mock_conn = mock.MagicMock( - connection_id="test_conn_id", - ) - mock_oob = mock.MagicMock() - - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as mock_save, mock.patch.object( - V10CredentialExchange, - "retrieve_by_connection_and_thread", - mock.CoroutineMock(), - ) as mock_retrieve: - mock_retrieve.return_value = stored_exchange - cx_rec = await self.manager.receive_request(request, mock_conn, mock_oob) - - mock_retrieve.assert_called_once_with( - self.session, - None, - request._thread_id, - role=V10CredentialExchange.ROLE_ISSUER, - for_update=True, - ) - mock_save.assert_called_once() - assert cx_rec.state == V10CredentialExchange.STATE_REQUEST_RECEIVED - assert cx_rec._credential_request.ser == INDY_CRED_REQ - assert cx_rec.connection_id == "test_conn_id" - - async def test_receive_request_no_cred_ex_with_offer_found(self): - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_OFFER_SENT, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - request = CredentialRequest( - requests_attach=[CredentialRequest.wrap_indy_cred_req(INDY_CRED_REQ)] - ) - - mock_conn = mock.MagicMock( - connection_id="test_conn_id", - ) - - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as mock_save, mock.patch.object( - V10CredentialExchange, - "retrieve_by_connection_and_thread", - mock.CoroutineMock(), - ) as mock_retrieve: - mock_retrieve.side_effect = (StorageNotFoundError(),) - with self.assertRaises(StorageNotFoundError): - cx_rec = await self.manager.receive_request(request, mock_conn, None) - - mock_retrieve.assert_called_once_with( - self.session, - "test_conn_id", - request._thread_id, - role=V10CredentialExchange.ROLE_ISSUER, - for_update=True, - ) - - async def test_issue_credential_revocable(self): - connection_id = "test_conn_id" - comment = "comment" - cred_values = {"attr": "value"} - thread_id = "thread-id" - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_offer=INDY_OFFER, - credential_request=INDY_CRED_REQ, - credential_proposal_dict=CredentialProposal( - credential_proposal=CredentialPreview.deserialize( - {"attributes": [{"name": "attr", "value": "value"}]} - ), - cred_def_id=CRED_DEF_ID, - schema_id=SCHEMA_ID, - ).serialize(), - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_REQUEST_RECEIVED, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - issuer = mock.MagicMock() - cred = {"indy": "credential"} - cred_rev_id = "1000" - issuer.create_credential = mock.CoroutineMock( - return_value=(json.dumps(cred), cred_rev_id) - ) - self.context.injector.bind_instance(IndyIssuer, issuer) - - with mock.patch.object( - test_module, "IndyRevocation", autospec=True - ) as revoc, mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex: - revoc.return_value.get_or_create_active_registry = mock.CoroutineMock( - return_value=( - mock.MagicMock( # active_rev_reg_rec - revoc_reg_id=REV_REG_ID, - ), - mock.MagicMock( # rev_reg - registry_id=REV_REG_ID, - tails_local_path="dummy-path", - get_or_fetch_local_tails_path=mock.CoroutineMock(), - max_creds=10, - ), - ) - ) - (ret_exchange, ret_cred_issue) = await self.manager.issue_credential( - stored_exchange, comment=comment, retries=1 - ) - - save_ex.assert_called_once() - - issuer.create_credential.assert_called_once_with( - SCHEMA, - INDY_OFFER, - INDY_CRED_REQ, - cred_values, - REV_REG_ID, - "dummy-path", - ) - - assert ret_exchange._credential.ser == cred - assert ret_cred_issue.indy_credential() == cred - assert ret_exchange.state == V10CredentialExchange.STATE_ISSUED - assert ret_cred_issue._thread_id == thread_id - - # cover case with existing cred - ( - ret_existing_exchange, - ret_existing_cred, - ) = await self.manager.issue_credential( - ret_exchange, comment=comment, retries=0 - ) - assert ret_existing_exchange == ret_exchange - assert ret_existing_cred._thread_id == thread_id - - async def test_issue_credential_non_revocable(self): - CRED_DEF_NR = deepcopy(CRED_DEF) - CRED_DEF_NR["value"]["revocation"] = None - connection_id = "test_conn_id" - comment = "comment" - cred_values = {"attr": "value"} - thread_id = "thread-id" - self.context.injector.bind_instance( - BaseMultitenantManager, - mock.MagicMock(MultitenantManager, autospec=True), - ) - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_offer=INDY_OFFER, - credential_request=INDY_CRED_REQ, - credential_proposal_dict=CredentialProposal( - credential_proposal=CredentialPreview.deserialize( - {"attributes": [{"name": "attr", "value": "value"}]} - ), - cred_def_id=CRED_DEF_ID, - schema_id=SCHEMA_ID, - ).serialize(), - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_REQUEST_RECEIVED, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - issuer = mock.MagicMock() - cred = {"indy": "credential"} - issuer.create_credential = mock.CoroutineMock( - return_value=(json.dumps(cred), None) - ) - self.context.injector.bind_instance(IndyIssuer, issuer) - - Ledger = mock.MagicMock() - self.ledger = Ledger() - self.ledger.get_schema = mock.CoroutineMock(return_value=SCHEMA) - self.ledger.get_credential_definition = mock.CoroutineMock( - return_value=CRED_DEF_NR - ) - self.ledger.__aenter__ = mock.CoroutineMock(return_value=self.ledger) - self.context.injector.clear_binding(BaseLedger) - self.context.injector.bind_instance(BaseLedger, self.ledger) - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - IndyLedgerRequestsExecutor, - "get_ledger_for_identifier", - mock.CoroutineMock(return_value=("test_ledger_id", self.ledger)), - ): - (ret_exchange, ret_cred_issue) = await self.manager.issue_credential( - stored_exchange, comment=comment, retries=0 - ) - - save_ex.assert_called_once() - - issuer.create_credential.assert_called_once_with( - SCHEMA, - INDY_OFFER, - INDY_CRED_REQ, - cred_values, - None, - None, - ) - - assert ret_exchange._credential.ser == cred - assert ret_cred_issue.indy_credential() == cred - assert ret_exchange.state == V10CredentialExchange.STATE_ISSUED - assert ret_cred_issue._thread_id == thread_id - - async def test_issue_credential_fills_rr(self): - connection_id = "test_conn_id" - comment = "comment" - cred_values = {"attr": "value"} - thread_id = "thread-id" - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_offer=INDY_OFFER, - credential_request=INDY_CRED_REQ, - credential_proposal_dict=CredentialProposal( - credential_proposal=CredentialPreview.deserialize( - {"attributes": [{"name": "attr", "value": "value"}]} - ), - cred_def_id=CRED_DEF_ID, - schema_id=SCHEMA_ID, - ).serialize(), - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_REQUEST_RECEIVED, - thread_id=thread_id, - revocation_id="1000", - new_with_id=True, - ) - await stored_exchange.save(self.session) - - issuer = mock.MagicMock() - cred = {"indy": "credential"} - issuer.create_credential = mock.CoroutineMock( - return_value=(json.dumps(cred), stored_exchange.revocation_id) - ) - self.context.injector.bind_instance(IndyIssuer, issuer) - - with mock.patch.object( - test_module, "IndyRevocation", autospec=True - ) as revoc, mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex: - revoc.return_value = mock.MagicMock( - get_or_create_active_registry=( - mock.CoroutineMock( - return_value=( - mock.MagicMock( # active_rev_reg_rec - revoc_reg_id=REV_REG_ID, - set_state=mock.CoroutineMock(), - ), - mock.MagicMock( # rev_reg - registry_id=REV_REG_ID, - tails_local_path="dummy-path", - max_creds=1000, - get_or_fetch_local_tails_path=(mock.CoroutineMock()), - ), - ) - ) - ), - handle_full_registry=mock.CoroutineMock(), - ) - (ret_exchange, ret_cred_issue) = await self.manager.issue_credential( - stored_exchange, comment=comment, retries=0 - ) - - save_ex.assert_called_once() - - issuer.create_credential.assert_called_once_with( - SCHEMA, - INDY_OFFER, - INDY_CRED_REQ, - cred_values, - REV_REG_ID, - "dummy-path", - ) - - revoc.return_value.handle_full_registry.assert_awaited_once_with(REV_REG_ID) - - assert ret_exchange._credential.ser == cred - assert ret_cred_issue.indy_credential() == cred - assert ret_exchange.state == V10CredentialExchange.STATE_ISSUED - assert ret_cred_issue._thread_id == thread_id - - async def test_issue_credential_request_bad_state(self): - connection_id = "test_conn_id" - thread_id = "thread-id" - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_offer=INDY_OFFER, - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_HOLDER, - state=V10CredentialExchange.STATE_PROPOSAL_SENT, - schema_id=SCHEMA_ID, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - with self.assertRaises(CredentialManagerError): - await self.manager.issue_credential(stored_exchange) - - async def test_issue_credential_no_active_rr_no_retries(self): - connection_id = "test_conn_id" - comment = "comment" - cred_values = {"attr": "value"} - thread_id = "thread-id" - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_offer=INDY_OFFER, - credential_request=INDY_CRED_REQ, - credential_proposal_dict=CredentialProposal( - credential_proposal=CredentialPreview.deserialize( - {"attributes": [{"name": "attr", "value": "value"}]} - ), - cred_def_id=CRED_DEF_ID, - schema_id=SCHEMA_ID, - ).serialize(), - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_REQUEST_RECEIVED, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - issuer = mock.MagicMock() - cred = {"indy": "credential"} - cred_rev_id = "1" - issuer.create_credential = mock.CoroutineMock( - return_value=(json.dumps(cred), cred_rev_id) - ) - self.context.injector.bind_instance(IndyIssuer, issuer) - self.context.injector.bind_instance( - IndyLedgerRequestsExecutor, - mock.MagicMock( - get_ledger_for_identifier=mock.CoroutineMock( - return_value=("test_ledger_id", self.ledger) - ) - ), - ) - with mock.patch.object(test_module, "IndyRevocation", autospec=True) as revoc: - revoc.return_value.get_or_create_active_registry = mock.CoroutineMock( - side_effect=[ - None, - ( - mock.MagicMock( # active_rev_reg_rec - revoc_reg_id=REV_REG_ID, - set_state=mock.CoroutineMock(), - ), - mock.MagicMock( # rev_reg - tails_local_path="dummy-path", - get_or_fetch_local_tails_path=(mock.CoroutineMock()), - ), - ), - ] - ) - with self.assertRaises(CredentialManagerError) as context: - await self.manager.issue_credential( - stored_exchange, comment=comment, retries=0 - ) - assert "has no active revocation registry" in context.message - - async def test_issue_credential_no_active_rr_retry(self): - connection_id = "test_conn_id" - comment = "comment" - cred_values = {"attr": "value"} - thread_id = "thread-id" - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_offer=INDY_OFFER, - credential_request=INDY_CRED_REQ, - credential_proposal_dict=CredentialProposal( - credential_proposal=CredentialPreview.deserialize( - {"attributes": [{"name": "attr", "value": "value"}]} - ), - cred_def_id=CRED_DEF_ID, - schema_id=SCHEMA_ID, - ).serialize(), - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - state=V10CredentialExchange.STATE_REQUEST_RECEIVED, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - issuer = mock.MagicMock() - cred = {"indy": "credential"} - cred_rev_id = "1" - issuer.create_credential = mock.CoroutineMock( - return_value=(json.dumps(cred), cred_rev_id) - ) - self.context.injector.bind_instance(IndyIssuer, issuer) - self.context.injector.bind_instance( - IndyLedgerRequestsExecutor, - mock.MagicMock( - get_ledger_for_identifier=mock.CoroutineMock( - return_value=("test_ledger_id", self.ledger) - ) - ), - ) - with mock.patch.object(test_module, "IndyRevocation", autospec=True) as revoc: - revoc.return_value.get_or_create_active_registry = mock.CoroutineMock( - return_value=None - ) - with self.assertRaises(CredentialManagerError) as context: - await self.manager.issue_credential( - stored_exchange, comment=comment, retries=1 - ) - assert "has no active revocation registry" in context.message - - async def test_receive_credential(self): - connection_id = "test_conn_id" - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_HOLDER, - state=V10CredentialExchange.STATE_REQUEST_SENT, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - issue = CredentialIssue( - credentials_attach=[CredentialIssue.wrap_indy_credential(INDY_CRED)] - ) - - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10CredentialExchange, - "retrieve_by_connection_and_thread", - mock.CoroutineMock(return_value=stored_exchange), - ) as retrieve_ex: - exchange = await self.manager.receive_credential(issue, connection_id) - - retrieve_ex.assert_called_once_with( - self.session, - connection_id, - issue._thread_id, - role=V10CredentialExchange.ROLE_HOLDER, - for_update=True, - ) - save_ex.assert_called_once() - - assert exchange._raw_credential.ser == INDY_CRED - assert exchange.state == V10CredentialExchange.STATE_CREDENTIAL_RECEIVED - - async def test_store_credential(self): - connection_id = "test_conn_id" - cred_req_meta = {"req": "meta"} - thread_id = "thread-id" - - preview = CredentialPreview( - attributes=( - CredAttrSpec( - name="legalName", value="value", mime_type="text/plain;lang=en-ca" - ), - CredAttrSpec(name="jurisdictionId", value="value"), - CredAttrSpec(name="incorporationDate", value="value"), - ) - ) - proposal = CredentialProposal( - credential_proposal=preview, cred_def_id=CRED_DEF_ID, schema_id=SCHEMA_ID - ) - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_request_metadata=cred_req_meta, - credential_proposal_dict=proposal, - raw_credential=INDY_CRED, - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_HOLDER, - state=V10CredentialExchange.STATE_CREDENTIAL_RECEIVED, - thread_id=thread_id, - auto_remove=True, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - cred_id = "cred-id" - holder = mock.MagicMock() - holder.store_credential = mock.CoroutineMock(return_value=cred_id) - holder.get_credential = mock.CoroutineMock( - return_value=json.dumps(INDY_CRED_INFO) - ) - self.context.injector.bind_instance(IndyHolder, holder) - self.context.injector.bind_instance( - IndyLedgerRequestsExecutor, - mock.MagicMock( - get_ledger_for_identifier=mock.CoroutineMock( - return_value=("test_ledger_id", self.ledger) - ) - ), - ) - with mock.patch.object( - test_module, "RevocationRegistry", autospec=True - ) as mock_rev_reg, mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10CredentialExchange, "delete_record", autospec=True - ) as delete_ex: - mock_rev_reg.from_definition = mock.MagicMock( - return_value=mock.MagicMock( - get_or_fetch_local_tails_path=mock.CoroutineMock() - ) - ) - ret_exchange = await self.manager.store_credential( - stored_exchange, credential_id=cred_id - ) - - save_ex.assert_called_once() - - self.ledger.get_credential_definition.assert_called_once_with(CRED_DEF_ID) - - holder.store_credential.assert_called_once_with( - CRED_DEF, - INDY_CRED, - cred_req_meta, - {"legalName": "text/plain;lang=en-ca"}, - credential_id=cred_id, - rev_reg_def=REV_REG_DEF, - ) - - holder.get_credential.assert_called_once_with(cred_id) - - assert ret_exchange.credential_id == cred_id - assert ret_exchange._credential.ser == INDY_CRED_INFO - assert ret_exchange.state == V10CredentialExchange.STATE_CREDENTIAL_RECEIVED - - async def test_store_credential_bad_state(self): - connection_id = "test_conn_id" - cred_req_meta = {"req": "meta"} - thread_id = "thread-id" - - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_request_metadata=cred_req_meta, - credential_proposal_dict=None, - raw_credential=INDY_CRED, - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_HOLDER, - state=V10CredentialExchange.STATE_OFFER_RECEIVED, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - cred_id = "cred-id" - - with self.assertRaises(CredentialManagerError): - await self.manager.store_credential(stored_exchange, credential_id=cred_id) - - async def test_store_credential_no_preview(self): - connection_id = "test_conn_id" - cred_req_meta = {"req": "meta"} - thread_id = "thread-id" - self.context.injector.bind_instance( - BaseMultitenantManager, - mock.MagicMock(MultitenantManager, autospec=True), - ) - cred_no_rev = {**INDY_CRED} - cred_no_rev["rev_reg_id"] = None - cred_no_rev["rev_reg"] = None - cred_no_rev["witness"] = None - cred_info_no_rev = {**INDY_CRED_INFO} - cred_info_no_rev["rev_reg_id"] = None - cred_info_no_rev["cred_rev_id"] = None - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_request_metadata=cred_req_meta, - credential_proposal_dict=None, - raw_credential=cred_no_rev, - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_HOLDER, - state=V10CredentialExchange.STATE_CREDENTIAL_RECEIVED, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - cred_def = mock.MagicMock() - self.ledger.get_credential_definition = mock.CoroutineMock(return_value=cred_def) - - cred_id = "cred-id" - holder = mock.MagicMock() - holder.store_credential = mock.CoroutineMock(return_value=cred_id) - holder.get_credential = mock.CoroutineMock( - return_value=json.dumps(cred_info_no_rev) - ) - self.context.injector.bind_instance(IndyHolder, holder) - self.context.injector.bind_instance( - IndyLedgerRequestsExecutor, - mock.MagicMock( - get_ledger_for_identifier=mock.CoroutineMock( - return_value=("test_ledger_id", self.ledger) - ) - ), - ) - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10CredentialExchange, "delete_record", autospec=True - ) as delete_ex: - ret_exchange = await self.manager.store_credential(stored_exchange) - - save_ex.assert_called_once() - - self.ledger.get_credential_definition.assert_called_once_with(CRED_DEF_ID) - - holder.store_credential.assert_called_once_with( - cred_def, - cred_no_rev, - cred_req_meta, - None, - credential_id=None, - rev_reg_def=None, - ) - - holder.get_credential.assert_called_once_with(cred_id) - - assert ret_exchange.credential_id == cred_id - assert ret_exchange._credential.ser == cred_info_no_rev - assert ret_exchange.state == V10CredentialExchange.STATE_CREDENTIAL_RECEIVED - - async def test_store_credential_holder_store_indy_error(self): - connection_id = "test_conn_id" - cred_req_meta = {"req": "meta"} - thread_id = "thread-id" - - cred_no_rev = {**INDY_CRED} - cred_no_rev["rev_reg_id"] = None - cred_no_rev["rev_reg"] = None - cred_no_rev["witness"] = None - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - credential_definition_id=CRED_DEF_ID, - credential_request_metadata=cred_req_meta, - credential_proposal_dict=None, - raw_credential=cred_no_rev, - initiator=V10CredentialExchange.INITIATOR_EXTERNAL, - role=V10CredentialExchange.ROLE_HOLDER, - state=V10CredentialExchange.STATE_CREDENTIAL_RECEIVED, - thread_id=thread_id, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - cred_def = mock.MagicMock() - self.ledger.get_credential_definition = mock.CoroutineMock(return_value=cred_def) - - cred_id = "cred-id" - holder = mock.MagicMock() - holder.store_credential = mock.CoroutineMock( - side_effect=test_module.IndyHolderError("Problem", {"message": "Nope"}) - ) - self.context.injector.bind_instance(IndyHolder, holder) - self.context.injector.bind_instance( - IndyLedgerRequestsExecutor, - mock.MagicMock( - get_ledger_for_identifier=mock.CoroutineMock( - return_value=("test_ledger_id", self.ledger) - ) - ), - ) - with self.assertRaises(test_module.IndyHolderError): - await self.manager.store_credential( - cred_ex_record=stored_exchange, credential_id=cred_id - ) - - async def test_send_credential_ack(self): - connection_id = "connection-id" - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - initiator=V10CredentialExchange.INITIATOR_SELF, - state=V10CredentialExchange.STATE_CREDENTIAL_RECEIVED, - thread_id="thid", - parent_thread_id="pthid", - role=V10CredentialExchange.ROLE_ISSUER, - trace=False, - auto_remove=True, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as mock_save_ex, mock.patch.object( - V10CredentialExchange, "delete_record", autospec=True - ) as mock_delete_ex, mock.patch.object( - test_module.LOGGER, "exception", mock.MagicMock() - ) as mock_log_exception, mock.patch.object( - test_module.LOGGER, "warning", mock.MagicMock() - ) as mock_log_warning: - mock_delete_ex.side_effect = test_module.StorageError() - (exch, ack) = await self.manager.send_credential_ack(stored_exchange) - assert ack._thread - mock_log_exception.assert_called_once() # cover exception log-and-continue - mock_log_warning.assert_called_once() # no BaseResponder - assert exch.state == V10CredentialExchange.STATE_ACKED - - mock_responder = MockResponder() # cover with responder - self.context.injector.bind_instance(BaseResponder, mock_responder) - (exch, ack) = await self.manager.send_credential_ack(stored_exchange) - assert ack._thread - assert exch.state == V10CredentialExchange.STATE_ACKED - - async def test_receive_credential_ack(self): - connection_id = "connection-id" - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - new_with_id=True, - ) - await stored_exchange.save(self.session) - - ack = CredentialAck() - - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10CredentialExchange, "delete_record", autospec=True - ) as delete_ex, mock.patch.object( - V10CredentialExchange, - "retrieve_by_connection_and_thread", - mock.CoroutineMock(), - ) as retrieve_ex: - retrieve_ex.return_value = stored_exchange - ret_exchange = await self.manager.receive_credential_ack(ack, connection_id) - - retrieve_ex.assert_called_once_with( - self.session, - connection_id, - ack._thread_id, - role=V10CredentialExchange.ROLE_ISSUER, - for_update=True, - ) - save_ex.assert_called_once() - - assert ret_exchange.state == V10CredentialExchange.STATE_ACKED - delete_ex.assert_called_once() - - async def test_receive_problem_report(self): - connection_id = "connection-id" - stored_exchange = V10CredentialExchange( - credential_exchange_id="dummy-cxid", - connection_id=connection_id, - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - new_with_id=True, - ) - await stored_exchange.save(self.session) - problem = CredentialProblemReport( - description={ - "code": test_module.ProblemReportReason.ISSUANCE_ABANDONED.value, - "en": "Insufficient privilege", - } - ) - - with mock.patch.object( - V10CredentialExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10CredentialExchange, - "retrieve_by_connection_and_thread", - mock.CoroutineMock(), - ) as retrieve_ex: - retrieve_ex.return_value = stored_exchange - - ret_exchange = await self.manager.receive_problem_report( - problem, connection_id - ) - retrieve_ex.assert_called_once_with( - self.session, connection_id, problem._thread_id, for_update=True - ) - save_ex.assert_called_once() - - assert ret_exchange.state == V10CredentialExchange.STATE_ABANDONED - - async def test_receive_problem_report_x(self): - connection_id = "connection-id" - problem = CredentialProblemReport( - description={ - "code": test_module.ProblemReportReason.ISSUANCE_ABANDONED.value, - "en": "Insufficient privilege", - } - ) - - with mock.patch.object( - V10CredentialExchange, - "retrieve_by_connection_and_thread", - mock.CoroutineMock(), - ) as retrieve_ex: - retrieve_ex.side_effect = test_module.StorageNotFoundError("No such record") - - exch = await self.manager.receive_problem_report(problem, connection_id) - assert exch is None - - async def test_retrieve_records(self): - self.cache = InMemoryCache() - self.session.context.injector.bind_instance(BaseCache, self.cache) - - for index in range(2): - exchange_record = V10CredentialExchange( - connection_id=str(index), - thread_id=str(1000 + index), - initiator=V10CredentialExchange.INITIATOR_SELF, - role=V10CredentialExchange.ROLE_ISSUER, - ) - await exchange_record.save(self.session) - - for i in range(2): # second pass gets from cache - for index in range(2): - ret_ex = await V10CredentialExchange.retrieve_by_connection_and_thread( - self.session, str(index), str(1000 + index) - ) - assert ret_ex.connection_id == str(index) - assert ret_ex.thread_id == str(1000 + index) diff --git a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py deleted file mode 100644 index d6ac8a0661..0000000000 --- a/aries_cloudagent/protocols/issue_credential/v1_0/tests/test_routes.py +++ /dev/null @@ -1,1452 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from .....admin.request_context import AdminRequestContext -from .....core.in_memory import InMemoryProfile -from .....wallet.base import BaseWallet -from .. import routes as test_module -from . import CRED_DEF_ID - - -class TestCredentialRoutes(IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.session_inject = {} - profile = InMemoryProfile.test_profile( - settings={ - "admin.admin_api_key": "secret-key", - } - ) - self.context = AdminRequestContext.test_context(self.session_inject, profile) - self.request_dict = { - "context": self.context, - "outbound_message_router": mock.CoroutineMock(), - } - self.request = mock.MagicMock( - app={}, - match_info={}, - query={}, - __getitem__=lambda _, k: self.request_dict[k], - headers={"x-api-key": "secret-key"}, - ) - - async def test_credential_exchange_list(self): - self.request.query = { - "thread_id": "dummy", - "connection_id": "dummy", - "role": "dummy", - "state": "dummy", - } - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.query = mock.CoroutineMock() - mock_cred_ex.query.return_value = [mock_cred_ex] - mock_cred_ex.serialize = mock.MagicMock() - mock_cred_ex.serialize.return_value = {"hello": "world"} - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.credential_exchange_list(self.request) - mock_response.assert_called_once_with( - {"results": [mock_cred_ex.serialize.return_value]} - ) - - async def test_credential_exchange_list_x(self): - self.request.query = { - "thread_id": "dummy", - "connection_id": "dummy", - "role": "dummy", - "state": "dummy", - } - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.query = mock.CoroutineMock( - side_effect=test_module.StorageError() - ) - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_list(self.request) - - async def test_credential_exchange_retrieve(self): - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value = mock_cred_ex - mock_cred_ex.serialize = mock.MagicMock() - mock_cred_ex.serialize.return_value = {"hello": "world"} - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.credential_exchange_retrieve(self.request) - mock_response.assert_called_once_with(mock_cred_ex.serialize.return_value) - - async def test_credential_exchange_retrieve_not_found(self): - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageNotFoundError() - ) - with self.assertRaises(test_module.web.HTTPNotFound): - await test_module.credential_exchange_retrieve(self.request) - - async def test_credential_exchange_retrieve_x(self): - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value = mock_cred_ex - mock_cred_ex.serialize = mock.MagicMock( - side_effect=test_module.BaseModelError() - ) - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_retrieve(self.request) - - async def test_credential_exchange_create(self): - self.request.json = mock.CoroutineMock() - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_connection_record, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module.CredentialPreview, "deserialize", autospec=True - ), mock.patch.object(test_module.web, "json_response") as mock_response: - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - - mock_credential_manager.return_value.create_offer.return_value = ( - mock.CoroutineMock(), - mock.CoroutineMock(), - ) - - mock_cred_ex_record = mock.MagicMock() - mock_cred_offer = mock.MagicMock() - - mock_credential_manager.return_value.prepare_send.return_value = ( - mock_cred_ex_record, - mock_cred_offer, - ) - - await test_module.credential_exchange_create(self.request) - - mock_response.assert_called_once_with( - mock_cred_ex_record.serialize.return_value - ) - - async def test_credential_exchange_create_x(self): - self.request.json = mock.CoroutineMock() - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_connection_record, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module.CredentialPreview, "deserialize", autospec=True - ), mock.patch.object(test_module.web, "json_response") as mock_response: - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - - mock_credential_manager.return_value.create_offer.return_value = ( - mock.CoroutineMock(), - mock.CoroutineMock(), - ) - - mock_cred_ex_record = mock.MagicMock() - mock_cred_offer = mock.MagicMock() - - mock_credential_manager.return_value.prepare_send.side_effect = ( - test_module.StorageError() - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_create(self.request) - - async def test_credential_exchange_create_no_proposal(self): - conn_id = "connection-id" - - self.request.json = mock.CoroutineMock(return_value={"connection_id": conn_id}) - - with self.assertRaises(test_module.web.HTTPBadRequest) as context: - await test_module.credential_exchange_create(self.request) - assert "credential_proposal" in str(context.exception) - - async def test_credential_exchange_send(self): - self.request.json = mock.CoroutineMock() - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module.CredentialPreview, "deserialize", autospec=True - ), mock.patch.object(test_module.web, "json_response") as mock_response: - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - - mock_credential_manager.return_value.create_offer.return_value = ( - mock.CoroutineMock(), - mock.CoroutineMock(), - ) - - mock_cred_ex_record = mock.MagicMock() - mock_cred_offer = mock.MagicMock() - - mock_credential_manager.return_value.prepare_send.return_value = ( - mock_cred_ex_record, - mock_cred_offer, - ) - - await test_module.credential_exchange_send(self.request) - - mock_response.assert_called_once_with( - mock_cred_ex_record.serialize.return_value - ) - - async def test_credential_exchange_send_no_proposal(self): - conn_id = "connection-id" - - self.request.json = mock.CoroutineMock(return_value={"connection_id": conn_id}) - - with self.assertRaises(test_module.web.HTTPBadRequest) as context: - await test_module.credential_exchange_send(self.request) - assert "credential_proposal" in str(context.exception) - - async def test_credential_exchange_send_no_conn_record(self): - conn_id = "connection-id" - preview_spec = {"attributes": [{"name": "attr", "value": "value"}]} - - self.request.json = mock.CoroutineMock( - return_value={"connection_id": conn_id, "credential_proposal": preview_spec} - ) - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager: - # Emulate storage not found (bad connection id) - mock_conn_rec.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageNotFoundError() - ) - - mock_credential_manager.return_value.create_offer.return_value = ( - mock.MagicMock(), - mock.MagicMock(), - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send(self.request) - - async def test_credential_exchange_send_not_ready(self): - conn_id = "connection-id" - preview_spec = {"attributes": [{"name": "attr", "value": "value"}]} - - self.request.json = mock.CoroutineMock( - return_value={"connection_id": conn_id, "credential_proposal": preview_spec} - ) - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager: - # Emulate connection not ready - mock_conn_rec.retrieve_by_id.return_value.is_ready = False - - mock_credential_manager.return_value.create_offer.return_value = ( - mock.MagicMock(), - mock.MagicMock(), - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.credential_exchange_send(self.request) - - async def test_credential_exchange_send_x(self): - self.request.json = mock.CoroutineMock() - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module.CredentialPreview, "deserialize", autospec=True - ): - mock_cred_ex_record = mock.MagicMock( - serialize=mock.MagicMock(side_effect=test_module.BaseModelError()), - save_error_state=mock.CoroutineMock(), - ) - mock_cred_offer = mock.MagicMock() - - mock_credential_manager.return_value = mock.MagicMock( - create_offer=mock.CoroutineMock( - return_value=( - mock.CoroutineMock(), - mock.CoroutineMock(), - ) - ), - prepare_send=mock.CoroutineMock( - return_value=( - mock_cred_ex_record, - mock_cred_offer, - ) - ), - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send(self.request) - - async def test_credential_exchange_send_proposal(self): - conn_id = "connection-id" - preview_spec = {"attributes": [{"name": "attr", "value": "value"}]} - - self.request.json = mock.CoroutineMock( - return_value={"connection_id": conn_id, "credential_proposal": preview_spec} - ) - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_cred_ex_record = mock.MagicMock() - mock_credential_manager.return_value.create_proposal.return_value = ( - mock_cred_ex_record - ) - await test_module.credential_exchange_send_proposal(self.request) - - self.request["outbound_message_router"].assert_awaited_once_with( - mock_cred_ex_record.credential_proposal_dict, connection_id=conn_id - ) - mock_response.assert_called_once_with( - mock_cred_ex_record.serialize.return_value - ) - - async def test_credential_exchange_send_proposal_no_conn_record(self): - self.request.json = mock.CoroutineMock() - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module.CredentialPreview, "deserialize", autospec=True - ) as mock_preview_deserialize: - # Emulate storage not found (bad connection id) - mock_conn_rec.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageNotFoundError() - ) - - mock_credential_manager.return_value.create_proposal.return_value = ( - mock.MagicMock() - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send_proposal(self.request) - - async def test_credential_exchange_send_proposal_deser_x(self): - conn_id = "connection-id" - preview_spec = {"attributes": [{"name": "attr", "value": "value"}]} - - self.request.json = mock.CoroutineMock( - return_value={"connection_id": conn_id, "credential_proposal": preview_spec} - ) - - with mock.patch.object( - test_module.CredentialPreview, "deserialize", autospec=True - ) as mock_preview_deser: - mock_preview_deser.side_effect = test_module.BaseModelError() - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send_proposal(self.request) - - async def test_credential_exchange_send_proposal_not_ready(self): - self.request.json = mock.CoroutineMock() - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module.CredentialPreview, "deserialize", autospec=True - ) as mock_preview_deserialize: - # Emulate connection not ready - mock_conn_rec.retrieve_by_id = mock.CoroutineMock() - mock_conn_rec.retrieve_by_id.return_value.is_ready = False - - mock_credential_manager.return_value.create_proposal.return_value = ( - mock.MagicMock() - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.credential_exchange_send_proposal(self.request) - - async def test_credential_exchange_send_proposal_x(self): - conn_id = "connection-id" - preview_spec = {"attributes": [{"name": "attr", "value": "value"}]} - - self.request.json = mock.CoroutineMock( - return_value={"connection_id": conn_id, "credential_proposal": preview_spec} - ) - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager: - mock_cred_ex_record = mock.MagicMock( - serialize=mock.MagicMock(side_effect=test_module.BaseModelError()), - save_error_state=mock.CoroutineMock(), - ) - mock_credential_manager.return_value.create_proposal.return_value = ( - mock_cred_ex_record - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send_proposal(self.request) - - async def test_credential_exchange_create_free_offer(self): - self.request.json = mock.CoroutineMock( - return_value={ - "auto_issue": False, - "cred_def_id": CRED_DEF_ID, - "credential_preview": { - "attributes": [{"name": "hello", "value": "world"}] - }, - } - ) - - self.context.update_settings({"debug.auto_respond_credential_offer": True}) - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - mock_cred_ex_record = mock.MagicMock() - mock_credential_manager.return_value.create_offer.return_value = ( - mock_cred_ex_record, - mock.MagicMock(), - ) - - await test_module.credential_exchange_create_free_offer(self.request) - - mock_response.assert_called_once_with( - mock_cred_ex_record.serialize.return_value - ) - - async def test_credential_exchange_create_free_offer_no_cred_def_id(self): - self.request.json = mock.CoroutineMock( - return_value={ - "auto_issue": False, - "credential_preview": { - "attributes": [{"name": "hello", "value": "world"}] - }, - } - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_create_free_offer(self.request) - - async def test_credential_exchange_create_free_offer_no_preview(self): - self.request.json = mock.CoroutineMock() - self.request.json.return_value = {"comment": "comment", "cred_def_id": "dummy"} - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_create_free_offer(self.request) - - async def test_credential_exchange_create_free_offer_no_conn_id_no_public_did(self): - self.request.json = mock.CoroutineMock( - return_value={ - "auto_issue": False, - "cred_def_id": CRED_DEF_ID, - "credential_preview": { - "attributes": [{"name": "hello", "value": "world"}] - }, - } - ) - - self.context.update_settings({"default_endpoint": "http://1.2.3.4:8081"}) - self.session_inject[BaseWallet] = mock.MagicMock( - get_public_did=mock.CoroutineMock(return_value=None), - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_create_free_offer(self.request) - - async def test_credential_exchange_create_free_offer_deser_x(self): - self.request.json = mock.CoroutineMock( - return_value={ - "auto_issue": False, - "cred_def_id": CRED_DEF_ID, - "credential_preview": { - "attributes": [{"name": "hello", "value": "world"}] - }, - } - ) - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager: - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - mock_cred_ex_record = mock.MagicMock() - mock_credential_manager.return_value.create_offer.side_effect = ( - test_module.BaseModelError() - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_create_free_offer(self.request) - - async def test_credential_exchange_create_free_offer_x(self): - self.request.json = mock.CoroutineMock( - return_value={ - "auto_issue": False, - "cred_def_id": CRED_DEF_ID, - "credential_preview": { - "attributes": [{"name": "hello", "value": "world"}] - }, - } - ) - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager: - mock_cred_ex_record = mock.MagicMock( - serialize=mock.MagicMock( - side_effect=test_module.BaseModelError(), - ), - save_error_state=mock.CoroutineMock(), - ) - mock_credential_manager.return_value = mock.MagicMock( - create_offer=mock.CoroutineMock( - return_value=( - mock_cred_ex_record, - mock.MagicMock(), - ) - ) - ) - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_create_free_offer(self.request) - - async def test_credential_exchange_send_free_offer(self): - self.request.json = mock.CoroutineMock( - return_value={ - "auto_issue": False, - "cred_def_id": CRED_DEF_ID, - "credential_preview": { - "attributes": [{"name": "hello", "value": "world"}] - }, - } - ) - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - - mock_cred_ex_record = mock.MagicMock() - - mock_credential_manager.return_value.create_offer.return_value = ( - mock_cred_ex_record, - mock.MagicMock(), - ) - - await test_module.credential_exchange_send_free_offer(self.request) - - mock_response.assert_called_once_with( - mock_cred_ex_record.serialize.return_value - ) - - async def test_credential_exchange_send_free_offer_no_cred_def_id(self): - self.request.json = mock.CoroutineMock() - self.request.json.return_value = { - "comment": "comment", - "credential_preview": "dummy", - } - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send_free_offer(self.request) - - async def test_credential_exchange_send_free_offer_no_preview(self): - self.request.json = mock.CoroutineMock() - self.request.json.return_value = { - "comment": "comment", - "cred_def_id": CRED_DEF_ID, - } - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send_free_offer(self.request) - - async def test_credential_exchange_send_free_offer_no_conn_record(self): - self.request.json = mock.CoroutineMock( - return_value={ - "auto_issue": False, - "cred_def_id": CRED_DEF_ID, - "credential_preview": "dummy", - } - ) - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager: - # Emulate storage not found (bad connection id) - mock_conn_rec.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageNotFoundError() - ) - - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - mock_credential_manager.return_value.create_offer.return_value = ( - mock.MagicMock(), - mock.MagicMock(), - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send_free_offer(self.request) - - async def test_credential_exchange_send_free_offer_not_ready(self): - self.request.json = mock.CoroutineMock() - self.request.json.return_value["auto_issue"] = True - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager: - # Emulate connection not ready - mock_conn_rec.retrieve_by_id = mock.CoroutineMock() - mock_conn_rec.retrieve_by_id.return_value.is_ready = False - - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - mock_credential_manager.return_value.create_offer.return_value = ( - mock.MagicMock(), - mock.MagicMock(), - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.credential_exchange_send_free_offer(self.request) - - async def test_credential_exchange_send_free_offer_x(self): - self.request.json = mock.CoroutineMock( - return_value={ - "auto_issue": False, - "cred_def_id": CRED_DEF_ID, - "credential_preview": { - "attributes": [{"name": "hello", "value": "world"}] - }, - } - ) - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_cred_ex_record = mock.MagicMock( - serialize=mock.MagicMock(side_effect=test_module.BaseModelError()), - save_error_state=mock.CoroutineMock(), - ) - - mock_credential_manager.return_value = mock.MagicMock( - create_offer=mock.CoroutineMock( - return_value=( - mock_cred_ex_record, - mock.MagicMock(), - ) - ) - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send_free_offer(self.request) - - async def test_credential_exchange_send_bound_offer(self): - self.request.json = mock.CoroutineMock(return_value={}) - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value.state = ( - mock_cred_ex.STATE_PROPOSAL_RECEIVED - ) - - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - - mock_cred_ex_record = mock.MagicMock() - - mock_credential_manager.return_value.create_offer.return_value = ( - mock_cred_ex_record, - mock.MagicMock(), - ) - - await test_module.credential_exchange_send_bound_offer(self.request) - - mock_response.assert_called_once_with( - mock_cred_ex_record.serialize.return_value - ) - - async def test_credential_exchange_send_bound_offer_bad_cred_ex_id(self): - self.request.json = mock.CoroutineMock(return_value={}) - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.side_effect = test_module.StorageNotFoundError() - - with self.assertRaises(test_module.web.HTTPNotFound): - await test_module.credential_exchange_send_bound_offer(self.request) - - async def test_credential_exchange_send_bound_offer_no_conn_record(self): - self.request.json = mock.CoroutineMock(return_value={}) - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_cred_ex.STATE_PROPOSAL_RECEIVED, - save_error_state=mock.CoroutineMock(), - ) - ) - - # Emulate storage not found (bad connection id) - mock_conn_rec.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageNotFoundError() - ) - - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - mock_credential_manager.return_value.create_offer.return_value = ( - mock.MagicMock(), - mock.MagicMock(), - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send_bound_offer(self.request) - - async def test_credential_exchange_send_bound_offer_bad_state(self): - self.request.json = mock.CoroutineMock(return_value={}) - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_cred_ex.STATE_ACKED, - save_error_state=mock.CoroutineMock(), - ) - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send_bound_offer(self.request) - - async def test_credential_exchange_send_bound_offer_not_ready(self): - self.request.json = mock.CoroutineMock(return_value={}) - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value.state = ( - mock_cred_ex.STATE_PROPOSAL_RECEIVED - ) - - # Emulate connection not ready - mock_conn_rec.retrieve_by_id = mock.CoroutineMock() - mock_conn_rec.retrieve_by_id.return_value.is_ready = False - - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - mock_credential_manager.return_value.create_offer.return_value = ( - mock.MagicMock(), - mock.MagicMock(), - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.credential_exchange_send_bound_offer(self.request) - - async def test_credential_exchange_send_request(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value.state = ( - mock_cred_ex.STATE_OFFER_RECEIVED - ) - - mock_cred_ex_record = mock.MagicMock() - - mock_credential_manager.return_value.create_request.return_value = ( - mock_cred_ex_record, - mock.MagicMock(), - ) - - await test_module.credential_exchange_send_request(self.request) - - mock_response.assert_called_once_with( - mock_cred_ex_record.serialize.return_value - ) - - async def test_credential_exchange_send_request_no_conn(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "OobRecord", autospec=True - ) as mock_oob_rec, mock.patch.object( - test_module, "default_did_from_verkey", autospec=True - ) as mock_default_did_from_verkey, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_oob_rec.retrieve_by_tag_filter = mock.CoroutineMock( - return_value=mock.MagicMock(our_recipient_key="our-recipient_key") - ) - mock_default_did_from_verkey.return_value = "holder-did" - - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value.state = ( - mock_cred_ex.STATE_OFFER_RECEIVED - ) - mock_cred_ex.retrieve_by_id.return_value.connection_id = None - - mock_cred_ex_record = mock.MagicMock() - - mock_credential_manager.return_value.create_request.return_value = ( - mock_cred_ex_record, - mock.MagicMock(), - ) - - await test_module.credential_exchange_send_request(self.request) - - mock_credential_manager.return_value.create_request.assert_called_once_with( - mock_cred_ex.retrieve_by_id.return_value, "holder-did" - ) - mock_response.assert_called_once_with( - mock_cred_ex_record.serialize.return_value - ) - mock_default_did_from_verkey.assert_called_once_with("our-recipient_key") - - async def test_credential_exchange_send_request_bad_cred_ex_id(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.side_effect = test_module.StorageNotFoundError() - - with self.assertRaises(test_module.web.HTTPNotFound): - await test_module.credential_exchange_send_request(self.request) - - async def test_credential_exchange_send_request_no_conn_record(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value = mock.MagicMock( - state=mock_cred_ex.STATE_OFFER_RECEIVED, - save_error_state=mock.CoroutineMock(), - ) - - # Emulate storage not found (bad connection id) - mock_conn_rec.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageNotFoundError() - ) - - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - mock_credential_manager.return_value.create_offer.return_value = ( - mock.MagicMock(), - mock.MagicMock(), - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_send_request(self.request) - - async def test_credential_exchange_send_request_not_ready(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value.state = ( - mock_cred_ex.STATE_OFFER_RECEIVED - ) - - # Emulate connection not ready - mock_conn_rec.retrieve_by_id = mock.CoroutineMock() - mock_conn_rec.retrieve_by_id.return_value.is_ready = False - - mock_credential_manager.return_value.create_offer = mock.CoroutineMock() - mock_credential_manager.return_value.create_offer.return_value = ( - mock.MagicMock(), - mock.MagicMock(), - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.credential_exchange_send_request(self.request) - - async def test_credential_exchange_issue(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value.state = ( - mock_cred_ex.STATE_REQUEST_RECEIVED - ) - - mock_cred_ex_record = mock.MagicMock() - - mock_credential_manager.return_value.issue_credential.return_value = ( - mock_cred_ex_record, - mock.MagicMock(), - ) - - await test_module.credential_exchange_issue(self.request) - - mock_response.assert_called_once_with( - mock_cred_ex_record.serialize.return_value - ) - - async def test_credential_exchange_issue_bad_cred_ex_id(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.side_effect = test_module.StorageNotFoundError() - - with self.assertRaises(test_module.web.HTTPNotFound): - await test_module.credential_exchange_issue(self.request) - - async def test_credential_exchange_issue_no_conn_record(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - mock_cred_ex_rec = mock.MagicMock( - connection_id="dummy", - serialize=mock.MagicMock(), - save_error_state=mock.CoroutineMock(), - ) - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex_cls: - mock_cred_ex_rec.state = mock_cred_ex_cls.STATE_REQUEST_RECEIVED - mock_cred_ex_cls.retrieve_by_id = mock.CoroutineMock( - return_value=mock_cred_ex_rec - ) - - # Emulate storage not found (bad connection id) - mock_conn_rec.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageNotFoundError() - ) - - mock_credential_manager.return_value.issue_credential = mock.CoroutineMock() - mock_credential_manager.return_value.issue_credential.return_value = ( - mock.MagicMock(), - mock.MagicMock(), - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_issue(self.request) - - async def test_credential_exchange_issue_not_ready(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value.state = ( - mock_cred_ex.STATE_REQUEST_RECEIVED - ) - - # Emulate connection not ready - mock_conn_rec.retrieve_by_id = mock.CoroutineMock() - mock_conn_rec.retrieve_by_id.return_value.is_ready = False - - mock_credential_manager.return_value.issue_credential = mock.CoroutineMock() - mock_credential_manager.return_value.issue_credential.return_value = ( - mock.MagicMock(), - mock.MagicMock(), - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.credential_exchange_issue(self.request) - - async def test_credential_exchange_issue_rev_reg_full(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - mock_cred_ex_rec = mock.MagicMock( - connection_id="dummy", - serialize=mock.MagicMock(), - save_error_state=mock.CoroutineMock(), - ) - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex_cls: - mock_cred_ex_cls.state = mock_cred_ex_cls.STATE_REQUEST_RECEIVED - mock_cred_ex_cls.retrieve_by_id = mock.CoroutineMock( - return_value=mock_cred_ex_rec - ) - - mock_conn_rec.retrieve_by_id = mock.CoroutineMock() - mock_conn_rec.retrieve_by_id.return_value.is_ready = True - - mock_issue_cred = mock.CoroutineMock( - side_effect=test_module.IndyIssuerError() - ) - mock_credential_manager.return_value.issue_credential = mock_issue_cred - - with self.assertRaises(test_module.web.HTTPBadRequest) as context: - await test_module.credential_exchange_issue(self.request) - - async def test_credential_exchange_issue_deser_x(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - mock_cred_ex_rec = mock.MagicMock( - connection_id="dummy", - serialize=mock.MagicMock(side_effect=test_module.BaseModelError()), - save_error_state=mock.CoroutineMock(), - ) - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex_cls: - mock_cred_ex_cls.retrieve_by_id = mock.CoroutineMock( - return_value=mock_cred_ex_rec - ) - mock_credential_manager.return_value = mock.MagicMock( - issue_credential=mock.CoroutineMock( - return_value=( - mock_cred_ex_rec, - mock.MagicMock(), - ) - ) - ) - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_issue(self.request) - - async def test_credential_exchange_store(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value.state = ( - mock_cred_ex.STATE_CREDENTIAL_RECEIVED - ) - - mock_cred_ex_record = mock.MagicMock() - - mock_credential_manager.return_value.store_credential.return_value = ( - mock_cred_ex_record - ) - mock_credential_manager.return_value.send_credential_ack.return_value = ( - mock_cred_ex_record, - mock.MagicMock(), - ) - - await test_module.credential_exchange_store(self.request) - - mock_response.assert_called_once_with( - mock_cred_ex_record.serialize.return_value - ) - - async def test_credential_exchange_store_bad_cred_id_json(self): - self.request.json = mock.CoroutineMock( - side_effect=test_module.JSONDecodeError("Nope", "Nope", 0) - ) - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value.state = ( - mock_cred_ex.STATE_CREDENTIAL_RECEIVED - ) - - mock_cred_ex_record = mock.MagicMock() - - mock_credential_manager.return_value.store_credential.return_value = ( - mock_cred_ex_record - ) - mock_credential_manager.return_value.send_credential_ack.return_value = ( - mock_cred_ex_record, - mock.MagicMock(), - ) - - await test_module.credential_exchange_store(self.request) - - mock_response.assert_called_once_with( - mock_cred_ex_record.serialize.return_value - ) - - async def test_credential_exchange_store_bad_cred_ex_id(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.side_effect = test_module.StorageNotFoundError() - - with self.assertRaises(test_module.web.HTTPNotFound): - await test_module.credential_exchange_store(self.request) - - async def test_credential_exchange_store_no_conn_record(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_cred_ex.STATE_CREDENTIAL_RECEIVED, - save_error_state=mock.CoroutineMock(), - ) - ) - - # Emulate storage not found (bad connection id) - mock_conn_rec.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageNotFoundError() - ) - - mock_credential_manager.return_value.store_credential.return_value = ( - mock_cred_ex - ) - mock_credential_manager.return_value.send_credential_ack.return_value = ( - mock_cred_ex, - mock.MagicMock(), - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_store(self.request) - - async def test_credential_exchange_store_not_ready(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.connection_id = "conn-123" - mock_cred_ex.thread_id = "conn-123" - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value.state = ( - mock_cred_ex.STATE_CREDENTIAL_RECEIVED - ) - - # Emulate connection not ready - mock_conn_rec.retrieve_by_id = mock.CoroutineMock() - mock_conn_rec.retrieve_by_id.return_value.is_ready = False - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.credential_exchange_store(self.request) - - async def test_credential_exchange_store_x(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_credential_manager, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex_cls, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_cred_ex_record = mock.MagicMock( - state=mock_cred_ex_cls.STATE_CREDENTIAL_RECEIVED, - serialize=mock.MagicMock(side_effect=test_module.BaseModelError()), - save_error_state=mock.CoroutineMock(), - ) - mock_cred_ex_cls.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock() - ) - - mock_credential_manager.return_value = mock.MagicMock( - store_credential=mock.CoroutineMock(return_value=mock_cred_ex_record), - send_credential_ack=mock.CoroutineMock( - return_value=(mock_cred_ex_record, mock.MagicMock()) - ), - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_store(self.request) - - async def test_credential_exchange_remove(self): - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_cred_ex.retrieve_by_id = mock.CoroutineMock() - mock_cred_ex.retrieve_by_id.return_value = mock_cred_ex - - mock_cred_ex.delete_record = mock.CoroutineMock() - - await test_module.credential_exchange_remove(self.request) - - mock_response.assert_called_once_with({}) - - async def test_credential_exchange_remove_bad_cred_ex_id(self): - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - # Emulate storage not found (bad cred ex id) - mock_cred_ex.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageNotFoundError() - ) - - with self.assertRaises(test_module.web.HTTPNotFound): - await test_module.credential_exchange_remove(self.request) - - async def test_credential_exchange_remove_x(self): - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - # Emulate storage not found (bad cred ex id) - mock_rec = mock.MagicMock( - delete_record=mock.CoroutineMock(side_effect=test_module.StorageError()) - ) - mock_cred_ex.retrieve_by_id = mock.CoroutineMock(return_value=mock_rec) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_remove(self.request) - - async def test_credential_exchange_problem_report(self): - self.request.json = mock.CoroutineMock( - return_value={"description": "Did I say no problem? I meant 'no: problem.'"} - ) - self.request.match_info = {"cred_ex_id": "dummy"} - magic_report = mock.MagicMock() - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr_cls, mock.patch.object( - test_module, "ConnRecord", autospec=True - ) as mock_conn_rec, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex, mock.patch.object( - test_module, "problem_report_for_record", mock.MagicMock() - ) as mock_problem_report, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - mock_cred_ex.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock(save_error_state=mock.CoroutineMock()) - ) - mock_problem_report.return_value = magic_report - - await test_module.credential_exchange_problem_report(self.request) - - self.request["outbound_message_router"].assert_awaited_once_with( - magic_report, - connection_id=mock_cred_ex.retrieve_by_id.return_value.connection_id, - ) - mock_response.assert_called_once_with({}) - - async def test_credential_exchange_problem_report_bad_cred_ex_id(self): - self.request.json = mock.CoroutineMock( - return_value={"description": "Did I say no problem? I meant 'no: problem.'"} - ) - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageNotFoundError() - ) - - with self.assertRaises(test_module.web.HTTPNotFound): - await test_module.credential_exchange_problem_report(self.request) - - async def test_credential_exchange_problem_report_x(self): - self.request.json = mock.CoroutineMock( - return_value={"description": "Did I say no problem? I meant 'no: problem.'"} - ) - self.request.match_info = {"cred_ex_id": "dummy"} - - with mock.patch.object( - test_module, "CredentialManager", autospec=True - ) as mock_cred_mgr_cls, mock.patch.object( - test_module, "problem_report_for_record", mock.MagicMock() - ) as mock_problem_report, mock.patch.object( - test_module, "V10CredentialExchange", autospec=True - ) as mock_cred_ex: - mock_cred_ex.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - save_error_state=mock.CoroutineMock( - side_effect=test_module.StorageError() - ) - ) - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.credential_exchange_problem_report(self.request) - - async def test_register(self): - mock_app = mock.MagicMock() - mock_app.add_routes = mock.MagicMock() - - await test_module.register(mock_app) - mock_app.add_routes.assert_called_once() - - async def test_post_process_routes(self): - mock_app = mock.MagicMock(_state={"swagger_dict": {}}) - test_module.post_process_routes(mock_app) - assert "tags" in mock_app._state["swagger_dict"] diff --git a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py index 747ff9db68..05581d1acb 100644 --- a/aries_cloudagent/protocols/issue_credential/v2_0/routes.py +++ b/aries_cloudagent/protocols/issue_credential/v2_0/routes.py @@ -1,7 +1,8 @@ """Credential exchange admin routes.""" -import logging from json.decoder import JSONDecodeError +import logging +import re from typing import Mapping, Optional from aiohttp import web @@ -14,11 +15,13 @@ ) from marshmallow import ValidationError, fields, validate, validates_schema +from . import problem_report_for_record, report_problem from ....admin.decorators.auth import tenant_authentication from ....admin.request_context import AdminRequestContext from ....anoncreds.holder import AnonCredsHolderError from ....anoncreds.issuer import AnonCredsIssuerError from ....connections.models.conn_record import ConnRecord +from ....core.event_bus import EventBus, EventWithMetadata from ....core.profile import Profile from ....indy.holder import IndyHolderError from ....indy.issuer import IndyIssuerError @@ -39,12 +42,12 @@ UUID4_EXAMPLE, UUID4_VALIDATE, ) +from ....revocation.models.issuer_cred_rev_record import IssuerCredRevRecord from ....storage.error import StorageError, StorageNotFoundError from ....utils.tracing import AdminAPIMessageTracingSchema, get_timer, trace_event from ....vc.ld_proofs.error import LinkedDataProofException from ....wallet.util import default_did_from_verkey from ...out_of_band.v1_0.models.oob_record import OobRecord -from . import problem_report_for_record, report_problem from .formats.handler import V20CredFormatError from .formats.ld_proof.models.cred_detail import LDProofVCDetailSchema from .manager import V20CredManager, V20CredManagerError @@ -1790,3 +1793,34 @@ def post_process_routes(app: web.Application): "externalDocs": {"description": "Specification", "url": SPEC_URI}, } ) + + +def register_events(bus: EventBus): + """Register event listeners.""" + bus.subscribe(re.compile(r"^acapy::cred-revoked$"), cred_revoked) + + +async def cred_revoked(profile: Profile, event: EventWithMetadata): + """Handle cred revoked event.""" + assert isinstance(event.payload, IssuerCredRevRecord) + rev_rec: IssuerCredRevRecord = event.payload + + if rev_rec.cred_ex_id is None: + return + + if ( + rev_rec.cred_ex_version + and rev_rec.cred_ex_version != IssuerCredRevRecord.VERSION_2 + ): + return + + async with profile.transaction() as txn: + try: + cred_ex_record = await V20CredExRecord.retrieve_by_id( + txn, rev_rec.cred_ex_id, for_update=True + ) + cred_ex_record.state = V20CredExRecord.STATE_CREDENTIAL_REVOKED + await cred_ex_record.save(txn, reason="revoke credential") + await txn.commit() + except StorageNotFoundError: + pass diff --git a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py index 08b982c4a2..f0dad4190f 100644 --- a/aries_cloudagent/protocols/out_of_band/v1_0/manager.py +++ b/aries_cloudagent/protocols/out_of_band/v1_0/manager.py @@ -34,9 +34,7 @@ from ...coordinate_mediation.v1_0.models.mediation_record import MediationRecord from ...didcomm_prefix import DIDCommPrefix from ...didexchange.v1_0.manager import DIDXManager -from ...issue_credential.v1_0.models.credential_exchange import V10CredentialExchange from ...issue_credential.v2_0.models.cred_ex_record import V20CredExRecord -from ...present_proof.v1_0.models.presentation_exchange import V10PresentationExchange from ...present_proof.v2_0.models.pres_exchange import V20PresExRecord from .message_types import DEFAULT_VERSION from .messages.invitation import HSProto, InvitationMessage @@ -201,33 +199,20 @@ async def create_attachment(self, attachment: Mapping, pthid: str) -> AttachDeco raise OutOfBandManagerError("Attachment must include type and id") async with self.profile.session() as session: + # TODO How do we enable using v1 offers? if a_type == "credential-offer": - try: - cred_ex_rec = await V10CredentialExchange.retrieve_by_id( - session, - a_id, - ) - message = cred_ex_rec.credential_offer_dict - - except StorageNotFoundError: - cred_ex_rec = await V20CredExRecord.retrieve_by_id( - session, - a_id, - ) - message = cred_ex_rec.cred_offer + cred_ex_rec = await V20CredExRecord.retrieve_by_id( + session, + a_id, + ) + message = cred_ex_rec.cred_offer + # TODO How do we enable using v1 requests? elif a_type == "present-proof": - try: - pres_ex_rec = await V10PresentationExchange.retrieve_by_id( - session, - a_id, - ) - message = pres_ex_rec.presentation_request_dict - except StorageNotFoundError: - pres_ex_rec = await V20PresExRecord.retrieve_by_id( - session, - a_id, - ) - message = pres_ex_rec.pres_request + pres_ex_rec = await V20PresExRecord.retrieve_by_id( + session, + a_id, + ) + message = pres_ex_rec.pres_request else: raise OutOfBandManagerError(f"Unknown attachment type: {a_type}") diff --git a/aries_cloudagent/protocols/present_proof/anoncreds/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/anoncreds/pres_exch_handler.py index 0052a78873..db93431d81 100644 --- a/aries_cloudagent/protocols/present_proof/anoncreds/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/anoncreds/pres_exch_handler.py @@ -3,7 +3,7 @@ import json import logging import time -from typing import Dict, Optional, Tuple, Union +from typing import Dict, Optional, Tuple from ....anoncreds.holder import AnonCredsHolder, AnonCredsHolderError from ....anoncreds.models.anoncreds_cred_def import CredDef @@ -14,9 +14,7 @@ from ....core.error import BaseError from ....core.profile import Profile from ....indy.models.xform import indy_proof_req2non_revoc_intervals -from ..v1_0.models.presentation_exchange import V10PresentationExchange -from ..v2_0.messages.pres_format import V20PresFormat -from ..v2_0.models.pres_exchange import V20PresExRecord +from ..indy.pres_exch_handler import IndyProofRequestContainer LOGGER = logging.getLogger(__name__) @@ -37,15 +35,8 @@ def __init__( self._profile = profile self.holder = AnonCredsHolder(profile) - def _extract_proof_request(self, pres_ex_record): - if isinstance(pres_ex_record, V20PresExRecord): - return pres_ex_record.pres_request.attachment(V20PresFormat.Format.INDY) - elif isinstance(pres_ex_record, V10PresentationExchange): - return pres_ex_record._presentation_request.ser - - raise TypeError( - "pres_ex_record must be V10PresentationExchange or V20PresExRecord" - ) + def _extract_proof_request(self, pres_ex_record: IndyProofRequestContainer): + return pres_ex_record.get_indy_proof_request() def _get_requested_referents( self, @@ -226,7 +217,7 @@ def _set_timestamps(self, requested_credentials: dict, requested_referents: dict async def return_presentation( self, - pres_ex_record: Union[V10PresentationExchange, V20PresExRecord], + pres_ex_record: IndyProofRequestContainer, requested_credentials: Optional[dict] = None, ) -> dict: """Return Indy proof request as dict.""" diff --git a/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py b/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py index 9c510afc9a..5337068095 100644 --- a/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py +++ b/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py @@ -3,7 +3,7 @@ import json import logging import time -from typing import Optional, Tuple, Union +from typing import Optional, Protocol, Tuple from ....core.error import BaseError from ....core.profile import Profile @@ -16,9 +16,6 @@ ) from ....multitenant.base import BaseMultitenantManager from ....revocation.models.revocation_registry import RevocationRegistry -from ..v1_0.models.presentation_exchange import V10PresentationExchange -from ..v2_0.messages.pres_format import V20PresFormat -from ..v2_0.models.pres_exchange import V20PresExRecord LOGGER = logging.getLogger(__name__) @@ -27,6 +24,14 @@ class IndyPresExchHandlerError(BaseError): """Base class for Indy Presentation Exchange related errors.""" +class IndyProofRequestContainer(Protocol): + """Protocol for a class that contains an Indy Proof Request.""" + + def get_indy_proof_request(self) -> dict: + """Retrieve proof request object.""" + ... + + class IndyPresExchHandler: """Base Presentation Exchange Handler.""" @@ -40,7 +45,7 @@ def __init__( async def return_presentation( self, - pres_ex_record: Union[V10PresentationExchange, V20PresExRecord], + pres_ex_record: IndyProofRequestContainer, requested_credentials: Optional[dict] = None, ) -> dict: """Return Indy proof request as dict.""" @@ -51,12 +56,8 @@ async def return_presentation( # extract credential ids and non_revoked requested_referents = {} - if isinstance(pres_ex_record, V20PresExRecord): - proof_request = pres_ex_record.pres_request.attachment( - V20PresFormat.Format.INDY - ) - elif isinstance(pres_ex_record, V10PresentationExchange): - proof_request = pres_ex_record._presentation_request.ser + proof_request = pres_ex_record.get_indy_proof_request() + non_revoc_intervals = indy_proof_req2non_revoc_intervals(proof_request) attr_creds = requested_credentials.get("requested_attributes", {}) req_attrs = proof_request.get("requested_attributes", {}) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/__init__.py b/aries_cloudagent/protocols/present_proof/v1_0/__init__.py deleted file mode 100644 index 0102a32839..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/__init__.py +++ /dev/null @@ -1,60 +0,0 @@ -from typing import Coroutine, Union - -from ....connections.models.conn_record import ConnRecord -from ....core.error import BaseError -from .messages.presentation_problem_report import ( - PresentationProblemReport, - ProblemReportReason, -) -from .models.presentation_exchange import V10PresentationExchange - - -def problem_report_for_record( - record: Union[ConnRecord, V10PresentationExchange], - desc_en: str, -) -> PresentationProblemReport: - """Create problem report for record. - - Args: - record: connection or exchange record - desc_en: description text to include in problem report - - """ - result = PresentationProblemReport( - description={ - "en": desc_en, - "code": ProblemReportReason.ABANDONED.value, - }, - ) - if record: - thid = getattr(record, "thread_id", None) - if thid: - result.assign_thread_id(thid) - - return result - - -async def report_problem( - err: BaseError, - desc_en: str, - http_error_class, - record: Union[ConnRecord, V10PresentationExchange], - outbound_handler: Coroutine, -): - """Send problem report response and raise corresponding HTTP error. - - Args: - err: error for internal diagnostics - desc_en: description text to include in problem report (response) - http_error_class: HTTP error to raise - record: record to cite by thread in problem report - outbound_handler: outbound message handler - - """ - if record: - await outbound_handler( - problem_report_for_record(record, desc_en), - connection_id=record.connection_id, - ) - - raise http_error_class(reason=err.roll_up) from err diff --git a/aries_cloudagent/protocols/present_proof/v1_0/controller.py b/aries_cloudagent/protocols/present_proof/v1_0/controller.py deleted file mode 100644 index 49650112a1..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/controller.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Protocol controller for present proof v1_0.""" - -from typing import Sequence - -VERIFY_VC = "aries.vc.verify" - - -class Controller: - """Present proof v1_0 protocol controller.""" - - def __init__(self, protocol: str): - """Initialize the controller.""" - - def determine_goal_codes(self) -> Sequence[str]: - """Return defined goal_codes.""" - return [VERIFY_VC] diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/__init__.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_ack_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_ack_handler.py deleted file mode 100644 index b5de11c169..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_ack_handler.py +++ /dev/null @@ -1,56 +0,0 @@ -"""Presentation ack message handler.""" - -from .....core.oob_processor import OobMessageProcessor -from .....messaging.base_handler import BaseHandler, HandlerException -from .....messaging.request_context import RequestContext -from .....messaging.responder import BaseResponder -from .....utils.tracing import get_timer, trace_event -from ..manager import PresentationManager -from ..messages.presentation_ack import PresentationAck - - -class PresentationAckHandler(BaseHandler): - """Message handler class for presentation acks.""" - - async def handle(self, context: RequestContext, responder: BaseResponder): - """Message handler logic for presentation acks. - - Args: - context: request context - responder: responder callback - """ - r_time = get_timer() - - self._logger.debug("PresentationAckHandler called with context %s", context) - assert isinstance(context.message, PresentationAck) - self._logger.info( - "Received presentation ack message: %s", - context.message.serialize(as_string=True), - ) - - # If connection is present it must be ready for use - if context.connection_record and not context.connection_ready: - raise HandlerException("Connection used for presentation ack not ready") - - # Find associated oob record - oob_processor = context.inject(OobMessageProcessor) - oob_record = await oob_processor.find_oob_record_for_inbound_message(context) - - # Either connection or oob context must be present - if not context.connection_record and not oob_record: - raise HandlerException( - "No connection or associated connectionless exchange found for" - " presentation ack" - ) - - presentation_manager = PresentationManager(context.profile) - await presentation_manager.receive_presentation_ack( - context.message, context.connection_record - ) - - trace_event( - context.settings, - context.message, - outcome="PresentationAckHandler.handle.END", - perf_counter=r_time, - ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py deleted file mode 100644 index 2329215b8e..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_handler.py +++ /dev/null @@ -1,94 +0,0 @@ -"""Presentation message handler.""" - -from .....core.oob_processor import OobMessageProcessor -from .....ledger.error import LedgerError -from .....messaging.base_handler import BaseHandler, HandlerException -from .....messaging.models.base import BaseModelError -from .....messaging.request_context import RequestContext -from .....messaging.responder import BaseResponder -from .....storage.error import StorageError -from .....utils.tracing import get_timer, trace_event -from .. import problem_report_for_record -from ..manager import PresentationManager -from ..messages.presentation import Presentation -from ..messages.presentation_problem_report import ProblemReportReason - - -class PresentationHandler(BaseHandler): - """Message handler class for presentations.""" - - async def handle(self, context: RequestContext, responder: BaseResponder): - """Message handler logic for presentations. - - Args: - context: request context - responder: responder callback - - """ - r_time = get_timer() - profile = context.profile - self._logger.debug("PresentationHandler called with context %s", context) - assert isinstance(context.message, Presentation) - self._logger.info( - "Received presentation message: %s", - context.message.serialize(as_string=True), - ) - - # If connection is present it must be ready for use - if context.connection_record and not context.connection_ready: - raise HandlerException("Connection used for presentation not ready") - - # Find associated oob record. If the presentation request was created as an oob - # attachment the presentation exchange record won't have a connection id (yet) - oob_processor = context.inject(OobMessageProcessor) - oob_record = await oob_processor.find_oob_record_for_inbound_message(context) - - # Normally we would do a check here that there is either a connection or - # an associated oob record. However as present proof supported receiving - # presentation without oob record or connection record - # (aip-1 style connectionless) we can't perform this check here - - presentation_manager = PresentationManager(profile) - - presentation_exchange_record = await presentation_manager.receive_presentation( - context.message, context.connection_record, oob_record - ) # mgr saves record state null if need be and possible - - r_time = trace_event( - context.settings, - context.message, - outcome="PresentationHandler.handle.END", - perf_counter=r_time, - ) - - # Automatically move to next state if flag is set - if ( - presentation_exchange_record - and presentation_exchange_record.auto_verify - or context.settings.get("debug.auto_verify_presentation") - ): - try: - await presentation_manager.verify_presentation( - presentation_exchange_record, responder - ) - except (BaseModelError, LedgerError, StorageError) as err: - self._logger.exception(err) - if presentation_exchange_record: - async with profile.session() as session: - await presentation_exchange_record.save_error_state( - session, - reason=err.roll_up, # us: be specific - ) - await responder.send_reply( - problem_report_for_record( - presentation_exchange_record, - ProblemReportReason.ABANDONED.value, # them: be vague - ) - ) - - trace_event( - context.settings, - presentation_exchange_record, - outcome="PresentationHandler.handle.VERIFY", - perf_counter=r_time, - ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_problem_report_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_problem_report_handler.py deleted file mode 100644 index c0dc0d5fae..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_problem_report_handler.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Presentation problem report message handler.""" - -from .....messaging.base_handler import BaseHandler, HandlerException -from .....messaging.request_context import RequestContext -from .....messaging.responder import BaseResponder -from .....storage.error import StorageError, StorageNotFoundError -from ..manager import PresentationManager -from ..messages.presentation_problem_report import PresentationProblemReport - - -class PresentationProblemReportHandler(BaseHandler): - """Message handler class for problem reports.""" - - async def handle(self, context: RequestContext, responder: BaseResponder): - """Message handler logic for problem reports. - - Args: - context: request context - responder: responder callback - """ - self._logger.debug( - "Present-proof v1.0 problem report handler called with context %s", - context, - ) - assert isinstance(context.message, PresentationProblemReport) - - # If connection is present it must be ready for use - if context.connection_record and not context.connection_ready: - raise HandlerException( - "Connection used for presentation problem report not ready" - ) - - presentation_manager = PresentationManager(context.profile) - try: - await presentation_manager.receive_problem_report( - context.message, - ( - context.connection_record.connection_id - if context.connection_record is not None - else None - ), - ) - except (StorageError, StorageNotFoundError): - self._logger.exception( - "Error processing present-proof v1.0 problem report message" - ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_proposal_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_proposal_handler.py deleted file mode 100644 index 952c830c00..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_proposal_handler.py +++ /dev/null @@ -1,88 +0,0 @@ -"""Presentation proposal message handler.""" - -from .....ledger.error import LedgerError -from .....messaging.base_handler import BaseHandler, HandlerException -from .....messaging.models.base import BaseModelError -from .....messaging.request_context import RequestContext -from .....messaging.responder import BaseResponder -from .....storage.error import StorageError -from .....utils.tracing import get_timer, trace_event -from .. import problem_report_for_record -from ..manager import PresentationManager -from ..messages.presentation_problem_report import ProblemReportReason -from ..messages.presentation_proposal import PresentationProposal - - -class PresentationProposalHandler(BaseHandler): - """Message handler class for presentation proposals.""" - - async def handle(self, context: RequestContext, responder: BaseResponder): - """Message handler logic for presentation proposals. - - Args: - context: proposal context - responder: responder callback - - """ - r_time = get_timer() - profile = context.profile - self._logger.debug("PresentationProposalHandler called with context %s", context) - assert isinstance(context.message, PresentationProposal) - self._logger.info( - "Received presentation proposal message: %s", - context.message.serialize(as_string=True), - ) - - if not context.connection_record: - raise HandlerException( - "Connectionless not supported for presentation proposal" - ) - # If connection is present it must be ready for use - elif not context.connection_ready: - raise HandlerException("Connection used for presentation proposal not ready") - - presentation_manager = PresentationManager(profile) - presentation_exchange_record = await presentation_manager.receive_proposal( - context.message, context.connection_record - ) # mgr only creates, saves record: on exception, saving state null is hopeless - - r_time = trace_event( - context.settings, - context.message, - outcome="PresentationProposalHandler.handle.END", - perf_counter=r_time, - ) - - # If auto_respond_presentation_proposal is set, reply with proof req - if context.settings.get("debug.auto_respond_presentation_proposal"): - presentation_request_message = None - try: - ( - presentation_exchange_record, - presentation_request_message, - ) = await presentation_manager.create_bound_request( - presentation_exchange_record=presentation_exchange_record, - comment=context.message.comment, - ) - await responder.send_reply(presentation_request_message) - except (BaseModelError, LedgerError, StorageError) as err: - self._logger.exception(err) - if presentation_exchange_record: - async with profile.session() as session: - await presentation_exchange_record.save_error_state( - session, - reason=err.roll_up, # us: be specific - ) - await responder.send_reply( - problem_report_for_record( - presentation_exchange_record, - ProblemReportReason.ABANDONED.value, # them: be vague - ) - ) - - trace_event( - context.settings, - presentation_request_message, - outcome="PresentationProposalHandler.handle.PRESENT", - perf_counter=r_time, - ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py deleted file mode 100644 index b026d5628c..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/presentation_request_handler.py +++ /dev/null @@ -1,167 +0,0 @@ -"""Presentation request message handler.""" - -from .....core.oob_processor import OobMessageProcessor -from .....indy.holder import IndyHolder, IndyHolderError -from .....indy.models.xform import indy_proof_req_preview2indy_requested_creds -from .....ledger.error import LedgerError -from .....messaging.base_handler import BaseHandler, HandlerException -from .....messaging.models.base import BaseModelError -from .....messaging.request_context import RequestContext -from .....messaging.responder import BaseResponder -from .....storage.error import StorageError, StorageNotFoundError -from .....utils.tracing import get_timer, trace_event -from .....wallet.error import WalletNotFoundError -from .. import problem_report_for_record -from ..manager import PresentationManager -from ..messages.presentation_problem_report import ProblemReportReason -from ..messages.presentation_request import PresentationRequest -from ..models.presentation_exchange import V10PresentationExchange - - -class PresentationRequestHandler(BaseHandler): - """Message handler class for Aries#0037 v1.0 presentation requests.""" - - async def handle(self, context: RequestContext, responder: BaseResponder): - """Message handler logic for Aries#0037 v1.0 presentation requests. - - Args: - context: request context - responder: responder callback - - """ - r_time = get_timer() - profile = context.profile - - self._logger.debug("PresentationRequestHandler called with context %s", context) - assert isinstance(context.message, PresentationRequest) - self._logger.info( - "Received presentation request message: %s", - context.message.serialize(as_string=True), - ) - - # If connection is present it must be ready for use - if context.connection_record and not context.connection_ready: - raise HandlerException("Connection used for presentation request not ready") - - # Find associated oob record - oob_processor = context.inject(OobMessageProcessor) - oob_record = await oob_processor.find_oob_record_for_inbound_message(context) - - # Either connection or oob context must be present - if not context.connection_record and not oob_record: - raise HandlerException( - "No connection or associated connectionless exchange found for" - " presentation request" - ) - - connection_id = ( - context.connection_record.connection_id if context.connection_record else None - ) - - presentation_manager = PresentationManager(profile) - - indy_proof_request = context.message.indy_proof_request(0) - - # Get presentation exchange record (holder initiated via proposal) - # or create it (verifier sent request first) - try: - async with profile.session() as session: - ( - presentation_exchange_record - ) = await V10PresentationExchange.retrieve_by_tag_filter( - session, - {"thread_id": context.message._thread_id}, - { - "role": V10PresentationExchange.ROLE_PROVER, - "connection_id": connection_id, - }, - ) # holder initiated via proposal - presentation_exchange_record.presentation_request = indy_proof_request - presentation_exchange_record.presentation_request_dict = ( - context.message.serialize() - ) - except StorageNotFoundError: # verifier sent this request free of any proposal - presentation_exchange_record = V10PresentationExchange( - connection_id=connection_id, - thread_id=context.message._thread_id, - initiator=V10PresentationExchange.INITIATOR_EXTERNAL, - role=V10PresentationExchange.ROLE_PROVER, - presentation_request=indy_proof_request, - presentation_request_dict=context.message.serialize(), - auto_present=context.settings.get( - "debug.auto_respond_presentation_request" - ), - trace=(context.message._trace is not None), - auto_remove=not profile.settings.get("preserve_exchange_records"), - ) - - presentation_exchange_record = await presentation_manager.receive_request( - presentation_exchange_record - ) # mgr only saves record: on exception, saving state null is hopeless - - r_time = trace_event( - context.settings, - context.message, - outcome="PresentationRequestHandler.handle.END", - perf_counter=r_time, - ) - - # If auto_present is enabled, respond immediately with presentation - if presentation_exchange_record.auto_present: - presentation_preview = None - if presentation_exchange_record.presentation_proposal_dict: - exchange_pres_proposal = ( - presentation_exchange_record.presentation_proposal_dict - ) - presentation_preview = exchange_pres_proposal.presentation_proposal - - try: - req_creds = await indy_proof_req_preview2indy_requested_creds( - indy_proof_request, - presentation_preview, - holder=context.inject(IndyHolder), - ) - except ValueError as err: - self._logger.warning(f"{err}") - return # not a protocol error: prover could still build proof manually - - presentation_message = None - try: - ( - presentation_exchange_record, - presentation_message, - ) = await presentation_manager.create_presentation( - presentation_exchange_record=presentation_exchange_record, - requested_credentials=req_creds, - comment="auto-presented for proof request nonce={}".format( - indy_proof_request["nonce"] - ), - ) - await responder.send_reply(presentation_message) - except ( - BaseModelError, - IndyHolderError, - LedgerError, - StorageError, - WalletNotFoundError, - ) as err: - self._logger.exception(err) - if presentation_exchange_record: - async with profile.session() as session: - await presentation_exchange_record.save_error_state( - session, - reason=err.roll_up, # us: be specific - ) - await responder.send_reply( - problem_report_for_record( - presentation_exchange_record, - ProblemReportReason.ABANDONED.value, # them: be vague - ) - ) - - trace_event( - context.settings, - presentation_message, - outcome="PresentationRequestHandler.handle.PRESENT", - perf_counter=r_time, - ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/__init__.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_ack_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_ack_handler.py deleted file mode 100644 index 4421ee9f2c..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_ack_handler.py +++ /dev/null @@ -1,84 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......core.oob_processor import OobMessageProcessor -from ......messaging.request_context import RequestContext -from ......messaging.responder import MockResponder -from ......transport.inbound.receipt import MessageReceipt -from ...messages.presentation_ack import PresentationAck -from .. import presentation_ack_handler as test_module - - -class TestPresentationAckHandler(IsolatedAsyncioTestCase): - async def test_called(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - session = request_context.session() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - mock_pres_mgr.return_value.receive_presentation_ack = mock.CoroutineMock() - request_context.message = PresentationAck() - request_context.connection_ready = True - request_context.connection_record = mock.MagicMock() - handler = test_module.PresentationAckHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_pres_mgr.assert_called_once_with(request_context.profile) - mock_pres_mgr.return_value.receive_presentation_ack.assert_called_once_with( - request_context.message, request_context.connection_record - ) - assert not responder.messages - - async def test_called_not_ready(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - mock_pres_mgr.return_value.receive_presentation_ack = mock.CoroutineMock() - request_context.message = PresentationAck() - request_context.connection_ready = False - handler = test_module.PresentationAckHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert err.exception.message == "Connection used for presentation ack not ready" - - assert not responder.messages - - async def test_called_no_connection_no_oob(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - # No oob record found - return_value=None - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - request_context.message = PresentationAck() - handler = test_module.PresentationAckHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "No connection or associated connectionless exchange found for presentation ack" - ) - - assert not responder.messages diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_handler.py deleted file mode 100644 index 97910ccaa1..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_handler.py +++ /dev/null @@ -1,110 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......core.oob_processor import OobMessageProcessor -from ......messaging.request_context import RequestContext -from ......messaging.responder import MockResponder -from ......transport.inbound.receipt import MessageReceipt -from ...messages.presentation import Presentation -from .. import presentation_handler as test_module - - -class TestPresentationHandler(IsolatedAsyncioTestCase): - async def test_called(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_verify_presentation"] = False - - oob_record = mock.MagicMock() - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=oob_record - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - mock_pres_mgr.return_value.receive_presentation = mock.CoroutineMock() - request_context.message = Presentation() - request_context.connection_ready = True - request_context.connection_record = mock.MagicMock() - handler = test_module.PresentationHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_pres_mgr.assert_called_once_with(request_context.profile) - mock_pres_mgr.return_value.receive_presentation.assert_called_once_with( - request_context.message, request_context.connection_record, oob_record - ) - assert not responder.messages - - async def test_called_auto_verify(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_verify_presentation"] = True - - oob_record = mock.MagicMock() - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=oob_record - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - mock_pres_mgr.return_value.receive_presentation = mock.CoroutineMock() - mock_pres_mgr.return_value.verify_presentation = mock.CoroutineMock() - request_context.message = Presentation() - request_context.connection_ready = True - request_context.connection_record = mock.MagicMock() - handler = test_module.PresentationHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_pres_mgr.assert_called_once_with(request_context.profile) - mock_pres_mgr.return_value.receive_presentation.assert_called_once_with( - request_context.message, request_context.connection_record, oob_record - ) - assert not responder.messages - - async def test_called_auto_verify_x(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_verify_presentation"] = True - - oob_record = mock.MagicMock() - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=oob_record - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - mock_pres_mgr.return_value = mock.MagicMock( - receive_presentation=mock.CoroutineMock( - return_value=mock.MagicMock(save_error_state=mock.CoroutineMock()) - ), - verify_presentation=mock.CoroutineMock( - side_effect=test_module.LedgerError() - ), - ) - - request_context.message = Presentation() - request_context.connection_ready = True - request_context.connection_record = mock.MagicMock() - handler = test_module.PresentationHandler() - responder = MockResponder() - - with mock.patch.object( - handler._logger, "exception", mock.MagicMock() - ) as mock_log_exc: - await handler.handle(request_context, responder) - mock_log_exc.assert_called_once() diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_problem_report_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_problem_report_handler.py deleted file mode 100644 index f0df55c1f3..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_problem_report_handler.py +++ /dev/null @@ -1,68 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......messaging.request_context import RequestContext -from ......messaging.responder import MockResponder -from ......transport.inbound.receipt import MessageReceipt -from ...messages.presentation_problem_report import ( - PresentationProblemReport, - ProblemReportReason, -) -from .. import presentation_problem_report_handler as test_module - - -class TestPresentationProblemReportHandler(IsolatedAsyncioTestCase): - async def test_called(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - request_context.connection_ready = True - mock_pres_mgr.return_value.receive_problem_report = mock.CoroutineMock() - request_context.message = PresentationProblemReport( - description={ - "en": "Change of plans", - "code": ProblemReportReason.ABANDONED.value, - } - ) - handler = test_module.PresentationProblemReportHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_pres_mgr.assert_called_once_with(request_context.profile) - mock_pres_mgr.return_value.receive_problem_report.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id - ) - assert not responder.messages - - async def test_called_x(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - request_context.connection_ready = True - mock_pres_mgr.return_value.receive_problem_report = mock.CoroutineMock( - side_effect=test_module.StorageError("Disk full") - ) - request_context.message = PresentationProblemReport( - description={ - "en": "Change of plans", - "code": ProblemReportReason.ABANDONED.value, - } - ) - handler = test_module.PresentationProblemReportHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_pres_mgr.assert_called_once_with(request_context.profile) - mock_pres_mgr.return_value.receive_problem_report.assert_called_once_with( - request_context.message, request_context.connection_record.connection_id - ) - assert not responder.messages diff --git a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_proposal_handler.py b/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_proposal_handler.py deleted file mode 100644 index 012763e58c..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_proposal_handler.py +++ /dev/null @@ -1,141 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......messaging.request_context import RequestContext -from ......messaging.responder import MockResponder -from ......transport.inbound.receipt import MessageReceipt -from ...messages.presentation_proposal import PresentationProposal -from .. import presentation_proposal_handler as test_module - - -class TestPresentationProposalHandler(IsolatedAsyncioTestCase): - async def test_called(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_respond_presentation_proposal"] = False - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - mock_pres_mgr.return_value.receive_proposal = mock.CoroutineMock( - return_value=mock.MagicMock() - ) - request_context.message = PresentationProposal() - request_context.connection_ready = True - request_context.connection_record = mock.MagicMock() - handler = test_module.PresentationProposalHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_pres_mgr.assert_called_once_with(request_context.profile) - mock_pres_mgr.return_value.receive_proposal.assert_called_once_with( - request_context.message, request_context.connection_record - ) - assert not responder.messages - - async def test_called_auto_request(self): - request_context = RequestContext.test_context() - request_context.message = mock.MagicMock() - request_context.message.comment = "hello world" - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_respond_presentation_proposal"] = True - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - mock_pres_mgr.return_value.receive_proposal = mock.CoroutineMock( - return_value="presentation_exchange_record" - ) - mock_pres_mgr.return_value.create_bound_request = mock.CoroutineMock( - return_value=( - mock_pres_mgr.return_value.receive_proposal.return_value, - "presentation_request_message", - ) - ) - request_context.message = PresentationProposal() - request_context.connection_ready = True - handler = test_module.PresentationProposalHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_pres_mgr.assert_called_once_with(request_context.profile) - mock_pres_mgr.return_value.create_bound_request.assert_called_once_with( - presentation_exchange_record=( - mock_pres_mgr.return_value.receive_proposal.return_value - ), - comment=request_context.message.comment, - ) - messages = responder.messages - assert len(messages) == 1 - (result, target) = messages[0] - assert result == "presentation_request_message" - assert target == {} - - async def test_called_auto_request_x(self): - request_context = RequestContext.test_context() - request_context.message = mock.MagicMock() - request_context.message.comment = "hello world" - request_context.message_receipt = MessageReceipt() - request_context.settings["debug.auto_respond_presentation_proposal"] = True - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - mock_pres_mgr.return_value.receive_proposal = mock.CoroutineMock( - return_value=mock.MagicMock(save_error_state=mock.CoroutineMock()) - ) - mock_pres_mgr.return_value.create_bound_request = mock.CoroutineMock( - side_effect=test_module.LedgerError() - ) - - request_context.message = PresentationProposal() - request_context.connection_ready = True - handler = test_module.PresentationProposalHandler() - responder = MockResponder() - - with mock.patch.object( - handler._logger, "exception", mock.MagicMock() - ) as mock_log_exc: - await handler.handle(request_context, responder) - mock_log_exc.assert_called_once() - - async def test_called_not_ready(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - mock_pres_mgr.return_value.receive_proposal = mock.CoroutineMock() - request_context.message = PresentationProposal() - request_context.connection_ready = False - handler = test_module.PresentationProposalHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message == "Connection used for presentation proposal not ready" - ) - - assert not responder.messages - - async def test_called_no_connection(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - - request_context.message = PresentationProposal() - handler = test_module.PresentationProposalHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "Connectionless not supported for presentation proposal" - ) - - assert not responder.messages 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 deleted file mode 100644 index 87eb0fc978..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/handlers/tests/test_presentation_request_handler.py +++ /dev/null @@ -1,951 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......core.oob_processor import OobMessageProcessor -from ......indy.holder import IndyHolder -from ......indy.models.pres_preview import ( - IndyPresAttrSpec, - IndyPresPredSpec, - IndyPresPreview, -) -from ......messaging.request_context import RequestContext -from ......messaging.responder import MockResponder -from ......storage.error import StorageNotFoundError -from ......transport.inbound.receipt import MessageReceipt -from .....didcomm_prefix import DIDCommPrefix -from ...messages.presentation_proposal import PresentationProposal -from ...messages.presentation_request import PresentationRequest -from .. import presentation_request_handler as test_module - -S_ID = "NcYxiDXkpYi6ov5FcYDi1e:2:vidya:1.0" -CD_ID = f"NcYxiDXkpYi6ov5FcYDi1e:3:CL:{S_ID}:tag1" -INDY_PROOF_REQ = { - "name": "proof-req", - "version": "1.0", - "nonce": "12345", - "requested_attributes": { - "0_player_uuid": { - "name": "player", - "restrictions": [{"cred_def_id": CD_ID}], - }, - "0_screencapture_uuid": { - "name": "screenCapture", - "restrictions": [{"cred_def_id": CD_ID}], - }, - }, - "requested_predicates": { - "0_highscore_GE_uuid": { - "name": "highScore", - "p_type": ">=", - "p_value": 1000000, - "restrictions": [{"cred_def_id": CD_ID}], - } - }, -} -PRES_PREVIEW = IndyPresPreview( - attributes=[ - IndyPresAttrSpec(name="player", cred_def_id=CD_ID, value="Richie Knucklez"), - IndyPresAttrSpec( - name="screenCapture", - cred_def_id=CD_ID, - mime_type="image/png", - value="aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - ), - ], - predicates=[ - IndyPresPredSpec( - name="highScore", cred_def_id=CD_ID, predicate=">=", threshold=1000000 - ) - ], -) - - -class TestPresentationRequestHandler(IsolatedAsyncioTestCase): - async def test_called(self): - request_context = RequestContext.test_context() - request_context.connection_record = mock.MagicMock() - request_context.connection_record.connection_id = "dummy" - request_context.message_receipt = MessageReceipt() - request_context.message = PresentationRequest() - request_context.message.indy_proof_request = mock.MagicMock( - return_value=INDY_PROOF_REQ - ) - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - px_rec_instance = test_module.V10PresentationExchange( - presentation_proposal_dict={ - "presentation_proposal": { - "@type": DIDCommPrefix.qualify_current( - "present-proof/1.0/presentation-preview" - ), - "attributes": [ - {"name": "favourite", "cred_def_id": CD_ID, "value": "potato"}, - {"name": "icon", "cred_def_id": CD_ID, "value": "cG90YXRv"}, - ], - "predicates": [], - } - }, - auto_present=True, - ) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls: - mock_pres_ex_cls.retrieve_by_tag_filter = mock.CoroutineMock( - return_value=px_rec_instance - ) - - mock_pres_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=mock.MagicMock() - ) - mock_pres_mgr.return_value.receive_request.return_value.auto_present = False - - request_context.connection_ready = True - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_pres_mgr.return_value.receive_request.assert_called_once_with( - px_rec_instance - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - assert not responder.messages - - async def test_called_not_found(self): - request_context = RequestContext.test_context() - request_context.connection_record = mock.MagicMock() - request_context.connection_record.connection_id = "dummy" - request_context.message_receipt = MessageReceipt() - request_context.message = PresentationRequest() - request_context.message.indy_proof_request = mock.MagicMock( - return_value=INDY_PROOF_REQ - ) - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - px_rec_instance = test_module.V10PresentationExchange( - presentation_proposal_dict={ - "presentation_proposal": { - "@type": DIDCommPrefix.qualify_current( - "present-proof/1.0/presentation-preview" - ), - "attributes": [ - {"name": "favourite", "cred_def_id": CD_ID, "value": "potato"}, - {"name": "icon", "cred_def_id": CD_ID, "value": "cG90YXRv"}, - ], - "predicates": [], - } - }, - auto_present=True, - ) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls: - mock_pres_ex_cls.retrieve_by_tag_filter = mock.CoroutineMock( - side_effect=StorageNotFoundError - ) - mock_pres_ex_cls.return_value = px_rec_instance - - mock_pres_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=mock.MagicMock() - ) - mock_pres_mgr.return_value.receive_request.return_value.auto_present = False - - request_context.connection_ready = True - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - - mock_pres_mgr.return_value.receive_request.assert_called_once_with( - px_rec_instance - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - assert not responder.messages - - async def test_called_auto_present(self): - request_context = RequestContext.test_context() - request_context.connection_record = mock.MagicMock() - request_context.connection_record.connection_id = "dummy" - request_context.message = PresentationRequest() - request_context.message.indy_proof_request = mock.MagicMock( - return_value={ - "name": "proof-request", - "version": "1.0", - "nonce": "1234567890", - "requested_attributes": { - "0_favourite_uuid": { - "name": "favourite", - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - }, - "1_icon_uuid": { - "name": "icon", - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - }, - }, - "requested_predicates": {}, - } - ) - request_context.message_receipt = MessageReceipt() - presentation_proposal = PresentationProposal( - comment="Hello World", presentation_proposal=PRES_PREVIEW - ) - px_rec_instance = test_module.V10PresentationExchange( - presentation_proposal_dict=presentation_proposal, - auto_present=True, - ) - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - mock_holder = mock.MagicMock( - get_credentials_for_presentation_request_by_referent=mock.CoroutineMock( - return_value=[{"cred_info": {"referent": "dummy"}}] - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - request_context.injector.bind_instance(IndyHolder, mock_holder) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance - mock_pres_ex_cls.retrieve_by_tag_filter = mock.CoroutineMock( - return_value=px_rec_instance - ) - mock_pres_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=px_rec_instance - ) - - mock_pres_mgr.return_value.create_presentation = mock.CoroutineMock( - return_value=(px_rec_instance, "presentation_message") - ) - request_context.connection_ready = True - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - mock_pres_mgr.return_value.create_presentation.assert_called_once() - - mock_pres_mgr.return_value.receive_request.assert_called_once_with( - px_rec_instance - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - messages = responder.messages - assert len(messages) == 1 - (result, target) = messages[0] - assert result == "presentation_message" - assert target == {} - - async def test_called_auto_present_x(self): - request_context = RequestContext.test_context() - request_context.connection_record = mock.MagicMock() - request_context.connection_record.connection_id = "dummy" - request_context.message = PresentationRequest() - request_context.message.indy_proof_request = mock.MagicMock( - return_value={ - "name": "proof-request", - "version": "1.0", - "nonce": "1234567890", - "requested_attributes": { - "0_favourite_uuid": { - "name": "favourite", - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - }, - "1_icon_uuid": { - "name": "icon", - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - }, - }, - "requested_predicates": {}, - } - ) - request_context.message_receipt = MessageReceipt() - presentation_proposal = PresentationProposal( - comment="Hello World", presentation_proposal=PRES_PREVIEW - ) - mock_px_rec = mock.MagicMock( - presentation_proposal_dict=presentation_proposal, - auto_present=True, - save_error_state=mock.CoroutineMock(), - ) - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - mock_holder = mock.MagicMock( - get_credentials_for_presentation_request_by_referent=( - mock.CoroutineMock(return_value=[{"cred_info": {"referent": "dummy"}}]) - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - request_context.injector.bind_instance(IndyHolder, mock_holder) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = mock_px_rec - mock_pres_ex_cls.retrieve_by_tag_filter = mock.CoroutineMock( - return_value=mock_px_rec - ) - mock_pres_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=mock_px_rec - ) - - mock_pres_mgr.return_value.create_presentation = mock.CoroutineMock( - side_effect=test_module.IndyHolderError() - ) - - request_context.connection_ready = True - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - - with mock.patch.object( - handler._logger, "exception", mock.MagicMock() - ) as mock_log_exc: - await handler.handle(request_context, responder) - mock_log_exc.assert_called_once() - - async def test_called_auto_present_no_preview(self): - request_context = RequestContext.test_context() - request_context.connection_record = mock.MagicMock() - request_context.connection_record.connection_id = "dummy" - request_context.message = PresentationRequest() - request_context.message.indy_proof_request = mock.MagicMock( - return_value={ - "name": "proof-request", - "version": "1.0", - "nonce": "1234567890", - "requested_attributes": { - "0_favourite_uuid": { - "name": "favourite", - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - }, - "1_icon_uuid": { - "name": "icon", - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - }, - }, - "requested_predicates": {}, - } - ) - request_context.message_receipt = MessageReceipt() - px_rec_instance = test_module.V10PresentationExchange(auto_present=True) - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - mock_holder = mock.MagicMock( - get_credentials_for_presentation_request_by_referent=( - mock.CoroutineMock( - return_value=[ - {"cred_info": {"referent": "dummy-0"}}, - {"cred_info": {"referent": "dummy-1"}}, - ] - ) - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - request_context.injector.bind_instance(IndyHolder, mock_holder) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance - mock_pres_ex_cls.retrieve_by_tag_filter = mock.CoroutineMock( - return_value=px_rec_instance - ) - mock_pres_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=px_rec_instance - ) - - mock_pres_mgr.return_value.create_presentation = mock.CoroutineMock( - return_value=(px_rec_instance, "presentation_message") - ) - request_context.connection_ready = True - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - mock_pres_mgr.return_value.create_presentation.assert_called_once() - - mock_pres_mgr.return_value.receive_request.assert_called_once_with( - px_rec_instance - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - messages = responder.messages - assert len(messages) == 1 - (result, target) = messages[0] - assert result == "presentation_message" - assert target == {} - - async def test_called_auto_present_pred_no_match(self): - request_context = RequestContext.test_context() - request_context.connection_record = mock.MagicMock() - request_context.connection_record.connection_id = "dummy" - request_context.message = PresentationRequest() - request_context.message.indy_proof_request = mock.MagicMock( - return_value={ - "name": "proof-request", - "version": "1.0", - "nonce": "1234567890", - "requested_attributes": {}, - "requested_predicates": { - "0_score_GE_uuid": { - "name": "score", - "p_type": ">=", - "p_value": 1000000, - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - } - }, - } - ) - request_context.message_receipt = MessageReceipt() - px_rec_instance = test_module.V10PresentationExchange(auto_present=True) - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - mock_holder = mock.MagicMock( - get_credentials_for_presentation_request_by_referent=( - mock.CoroutineMock(return_value=[]) - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - request_context.injector.bind_instance(IndyHolder, mock_holder) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance - mock_pres_ex_cls.retrieve_by_tag_filter = mock.CoroutineMock( - return_value=px_rec_instance - ) - mock_pres_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=px_rec_instance - ) - - mock_pres_mgr.return_value.create_presentation = mock.CoroutineMock( - return_value=(px_rec_instance, "presentation_message") - ) - request_context.connection_ready = True - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - mock_pres_mgr.return_value.create_presentation.assert_not_called() - - mock_pres_mgr.return_value.receive_request.assert_called_once_with( - px_rec_instance - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - assert not responder.messages - - async def test_called_auto_present_pred_single_match(self): - request_context = RequestContext.test_context() - request_context.connection_record = mock.MagicMock() - request_context.connection_record.connection_id = "dummy" - request_context.message = PresentationRequest() - request_context.message.indy_proof_request = mock.MagicMock( - return_value={ - "name": "proof-request", - "version": "1.0", - "nonce": "1234567890", - "requested_attributes": {}, - "requested_predicates": { - "0_score_GE_uuid": { - "name": "score", - "p_type": ">=", - "p_value": 1000000, - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - } - }, - } - ) - request_context.message_receipt = MessageReceipt() - px_rec_instance = test_module.V10PresentationExchange(auto_present=True) - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - mock_holder = mock.MagicMock( - get_credentials_for_presentation_request_by_referent=( - mock.CoroutineMock(return_value=[{"cred_info": {"referent": "dummy-0"}}]) - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - request_context.injector.bind_instance(IndyHolder, mock_holder) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance - mock_pres_ex_cls.retrieve_by_tag_filter = mock.CoroutineMock( - return_value=px_rec_instance - ) - mock_pres_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=px_rec_instance - ) - - mock_pres_mgr.return_value.create_presentation = mock.CoroutineMock( - return_value=(px_rec_instance, "presentation_message") - ) - request_context.connection_ready = True - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - mock_pres_mgr.return_value.create_presentation.assert_called_once() - - mock_pres_mgr.return_value.receive_request.assert_called_once_with( - px_rec_instance - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - messages = responder.messages - assert len(messages) == 1 - (result, target) = messages[0] - assert result == "presentation_message" - assert target == {} - - async def test_called_auto_present_pred_multi_match(self): - request_context = RequestContext.test_context() - request_context.connection_record = mock.MagicMock() - request_context.connection_record.connection_id = "dummy" - request_context.message = PresentationRequest() - request_context.message.indy_proof_request = mock.MagicMock( - return_value={ - "name": "proof-request", - "version": "1.0", - "nonce": "1234567890", - "requested_attributes": {}, - "requested_predicates": { - "0_score_GE_uuid": { - "name": "score", - "p_type": ">=", - "p_value": 1000000, - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - } - }, - } - ) - request_context.message_receipt = MessageReceipt() - px_rec_instance = test_module.V10PresentationExchange(auto_present=True) - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - mock_holder = mock.MagicMock( - get_credentials_for_presentation_request_by_referent=( - mock.CoroutineMock( - return_value=[ - {"cred_info": {"referent": "dummy-0"}}, - {"cred_info": {"referent": "dummy-1"}}, - ] - ) - ) - ) - request_context.injector.bind_instance(IndyHolder, mock_holder) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance - mock_pres_ex_cls.retrieve_by_tag_filter = mock.CoroutineMock( - return_value=px_rec_instance - ) - mock_pres_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=px_rec_instance - ) - - mock_pres_mgr.return_value.create_presentation = mock.CoroutineMock( - return_value=(px_rec_instance, "presentation_message") - ) - request_context.connection_ready = True - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - mock_pres_mgr.return_value.create_presentation.assert_called_once() - - mock_pres_mgr.return_value.receive_request.assert_called_once_with( - px_rec_instance - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - messages = responder.messages - assert len(messages) == 1 - (result, target) = messages[0] - assert result == "presentation_message" - assert target == {} - - async def test_called_auto_present_multi_cred_match_reft(self): - request_context = RequestContext.test_context() - request_context.connection_record = mock.MagicMock() - request_context.connection_record.connection_id = "dummy" - request_context.message = PresentationRequest() - request_context.message.indy_proof_request = mock.MagicMock( - return_value={ - "name": "proof-request", - "version": "1.0", - "nonce": "1234567890", - "requested_attributes": { - "0_favourite_uuid": { - "name": "favourite", - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - }, - "1_icon_uuid": { - "name": "icon", - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - }, - }, - "requested_predicates": {}, - } - ) - request_context.message_receipt = MessageReceipt() - px_rec_instance = test_module.V10PresentationExchange( - presentation_proposal_dict={ - "presentation_proposal": { - "@type": DIDCommPrefix.qualify_current( - "present-proof/1.0/presentation-preview" - ), - "attributes": [ - {"name": "favourite", "cred_def_id": CD_ID, "value": "potato"}, - {"name": "icon", "cred_def_id": CD_ID, "value": "cG90YXRv"}, - ], - "predicates": [], - } - }, - auto_present=True, - ) - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - mock_holder = mock.MagicMock( - get_credentials_for_presentation_request_by_referent=( - mock.CoroutineMock( - return_value=[ - { - "cred_info": { - "referent": "dummy-0", - "cred_def_id": CD_ID, - "attrs": { - "ident": "zero", - "favourite": "potato", - "icon": "cG90YXRv", - }, - } - }, - { - "cred_info": { - "referent": "dummy-1", - "cred_def_id": CD_ID, - "attrs": { - "ident": "one", - "favourite": "spud", - "icon": "c3B1ZA==", - }, - } - }, - { - "cred_info": { - "referent": "dummy-2", - "cred_def_id": CD_ID, - "attrs": { - "ident": "two", - "favourite": "patate", - "icon": "cGF0YXRl", - }, - } - }, - ] - ) - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - request_context.injector.bind_instance(IndyHolder, mock_holder) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance - mock_pres_ex_cls.retrieve_by_tag_filter = mock.CoroutineMock( - return_value=px_rec_instance - ) - mock_pres_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=px_rec_instance - ) - - mock_pres_mgr.return_value.create_presentation = mock.CoroutineMock( - return_value=(px_rec_instance, "presentation_message") - ) - request_context.connection_ready = True - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - await handler.handle(request_context, responder) - mock_pres_mgr.return_value.create_presentation.assert_called_once() - - mock_pres_mgr.return_value.receive_request.assert_called_once_with( - px_rec_instance - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - messages = responder.messages - assert len(messages) == 1 - (result, target) = messages[0] - assert result == "presentation_message" - assert target == {} - - async def test_called_auto_present_bait_and_switch(self): - request_context = RequestContext.test_context() - request_context.connection_record = mock.MagicMock() - request_context.connection_record.connection_id = "dummy" - request_context.message = PresentationRequest() - request_context.message.indy_proof_request = mock.MagicMock( - return_value={ - "name": "proof-request", - "version": "1.0", - "nonce": "1234567890", - "requested_attributes": { - "0_favourite_uuid": { - "name": "favourite", - "restrictions": [ - { - "cred_def_id": CD_ID, - } - ], - } - }, - "requested_predicates": {}, - } - ) - request_context.message_receipt = MessageReceipt() - px_rec_instance = test_module.V10PresentationExchange( - presentation_proposal_dict={ - "presentation_proposal": { - "@type": DIDCommPrefix.qualify_current( - "present-proof/1.0/presentation-preview" - ), - "attributes": [ - {"name": "favourite", "cred_def_id": CD_ID, "value": "potato"} - ], - "predicates": [], - } - }, - auto_present=True, - ) - - by_reft = mock.CoroutineMock( - return_value=[ - { - "cred_info": { - "referent": "dummy-0", - "cred_def_id": CD_ID, - "attrs": {"ident": "zero", "favourite": "yam"}, - } - }, - { - "cred_info": { - "referent": "dummy-1", - "cred_def_id": CD_ID, - "attrs": {"ident": "one", "favourite": "turnip"}, - } - }, - { - "cred_info": { - "referent": "dummy-2", - "cred_def_id": CD_ID, - "attrs": { - "ident": "two", - "favourite": "the idea of a potato but not a potato", - }, - } - }, - ] - ) - mock_holder = mock.MagicMock( - get_credentials_for_presentation_request_by_referent=by_reft - ) - request_context.injector.bind_instance(IndyHolder, mock_holder) - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - return_value=mock.MagicMock() - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr, mock.patch.object( - test_module, "V10PresentationExchange", autospec=True - ) as mock_pres_ex_cls: - mock_pres_ex_cls.return_value = px_rec_instance - mock_pres_ex_cls.retrieve_by_tag_filter = mock.CoroutineMock( - return_value=px_rec_instance - ) - mock_pres_mgr.return_value.receive_request = mock.CoroutineMock( - return_value=px_rec_instance - ) - - mock_pres_mgr.return_value.create_presentation = mock.CoroutineMock( - return_value=(px_rec_instance, "presentation_message") - ) - request_context.connection_ready = True - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - - await handler.handle(request_context, responder) - mock_pres_mgr.return_value.create_presentation.assert_not_called() - - mock_pres_mgr.return_value.receive_request.assert_called_once_with( - px_rec_instance - ) - mock_oob_processor.find_oob_record_for_inbound_message.assert_called_once_with( - request_context - ) - assert not responder.messages - - async def test_called_not_ready(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - request_context.connection_record = mock.MagicMock() - - with mock.patch.object( - test_module, "PresentationManager", autospec=True - ) as mock_pres_mgr: - mock_pres_mgr.return_value.receive_request = mock.CoroutineMock() - request_context.message = PresentationRequest() - request_context.connection_ready = False - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "Connection used for presentation request not ready" - ) - - assert not responder.messages - - async def test_no_conn_no_oob(self): - request_context = RequestContext.test_context() - request_context.message_receipt = MessageReceipt() - - mock_oob_processor = mock.MagicMock( - find_oob_record_for_inbound_message=mock.CoroutineMock( - # No oob record found - return_value=None - ) - ) - request_context.injector.bind_instance(OobMessageProcessor, mock_oob_processor) - - request_context.message = PresentationRequest() - handler = test_module.PresentationRequestHandler() - responder = MockResponder() - with self.assertRaises(test_module.HandlerException) as err: - await handler.handle(request_context, responder) - assert ( - err.exception.message - == "No connection or associated connectionless exchange found for presentation request" - ) - - assert not responder.messages diff --git a/aries_cloudagent/protocols/present_proof/v1_0/manager.py b/aries_cloudagent/protocols/present_proof/v1_0/manager.py deleted file mode 100644 index b89aad435c..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/manager.py +++ /dev/null @@ -1,565 +0,0 @@ -"""Classes to manage presentations.""" - -import json -import logging -from typing import Optional - -from ....connections.models.conn_record import ConnRecord -from ....core.error import BaseError -from ....core.profile import Profile -from ....indy.verifier import IndyVerifier -from ....messaging.decorators.attach_decorator import AttachDecorator -from ....messaging.responder import BaseResponder -from ....storage.error import StorageNotFoundError -from ...out_of_band.v1_0.models.oob_record import OobRecord -from ..indy.pres_exch_handler import IndyPresExchHandler -from .message_types import ATTACH_DECO_IDS, PRESENTATION, PRESENTATION_REQUEST -from .messages.presentation import Presentation -from .messages.presentation_ack import PresentationAck -from .messages.presentation_problem_report import ( - PresentationProblemReport, - ProblemReportReason, -) -from .messages.presentation_proposal import PresentationProposal -from .messages.presentation_request import PresentationRequest -from .models.presentation_exchange import V10PresentationExchange - -LOGGER = logging.getLogger(__name__) - - -class PresentationManagerError(BaseError): - """Presentation error.""" - - -class PresentationManager: - """Class for managing presentations.""" - - def __init__(self, profile: Profile): - """Initialize a PresentationManager. - - Args: - profile: The profile instance for this presentation manager - """ - - self._profile = profile - - async def create_exchange_for_proposal( - self, - connection_id: str, - presentation_proposal_message: PresentationProposal, - auto_present: Optional[bool] = None, - auto_remove: Optional[bool] = None, - ): - """Create a presentation exchange record for input presentation proposal. - - Args: - connection_id: connection identifier - presentation_proposal_message: presentation proposal to serialize - to exchange record - auto_present: whether to present proof upon receiving proof request - (default to configuration setting) - auto_remove: whether to remove this presentation exchange upon completion - - Returns: - Presentation exchange record, created - - """ - if auto_remove is None: - auto_remove = not self._profile.settings.get("preserve_exchange_records") - presentation_exchange_record = V10PresentationExchange( - connection_id=connection_id, - thread_id=presentation_proposal_message._thread_id, - initiator=V10PresentationExchange.INITIATOR_SELF, - role=V10PresentationExchange.ROLE_PROVER, - state=V10PresentationExchange.STATE_PROPOSAL_SENT, - presentation_proposal_dict=presentation_proposal_message, - auto_present=auto_present, - trace=(presentation_proposal_message._trace is not None), - auto_remove=auto_remove, - ) - async with self._profile.session() as session: - await presentation_exchange_record.save( - session, reason="create presentation proposal" - ) - - return presentation_exchange_record - - async def receive_proposal( - self, message: PresentationProposal, connection_record: ConnRecord - ): - """Receive a presentation proposal from message in context on manager creation. - - Returns: - Presentation exchange record, created - - """ - presentation_exchange_record = V10PresentationExchange( - connection_id=connection_record.connection_id, - thread_id=message._thread_id, - initiator=V10PresentationExchange.INITIATOR_EXTERNAL, - role=V10PresentationExchange.ROLE_VERIFIER, - state=V10PresentationExchange.STATE_PROPOSAL_RECEIVED, - presentation_proposal_dict=message, - trace=(message._trace is not None), - auto_remove=not self._profile.settings.get("preserve_exchange_records"), - ) - async with self._profile.session() as session: - await presentation_exchange_record.save( - session, reason="receive presentation request" - ) - - return presentation_exchange_record - - async def create_bound_request( - self, - presentation_exchange_record: V10PresentationExchange, - name: Optional[str] = None, - version: Optional[str] = None, - nonce: Optional[str] = None, - comment: Optional[str] = None, - ): - """Create a presentation request bound to a proposal. - - Args: - presentation_exchange_record: Presentation exchange record for which - to create presentation request - name: name to use in presentation request (None for default) - version: version to use in presentation request (None for default) - nonce: nonce to use in presentation request (None to generate) - comment: Optional human-readable comment pertaining to request creation - - Returns: - A tuple (updated presentation exchange record, presentation request message) - - """ - indy_proof_request = await ( - presentation_exchange_record.presentation_proposal_dict - ).presentation_proposal.indy_proof_request( - name=name, - version=version, - nonce=nonce, - profile=self._profile, - ) - presentation_request_message = PresentationRequest( - comment=comment, - request_presentations_attach=[ - AttachDecorator.data_base64( - mapping=indy_proof_request, - ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST], - ) - ], - ) - presentation_request_message._thread = { - "thid": presentation_exchange_record.thread_id - } - presentation_request_message.assign_trace_decorator( - self._profile.settings, presentation_exchange_record.trace - ) - - presentation_exchange_record.thread_id = presentation_request_message._thread_id - presentation_exchange_record.state = V10PresentationExchange.STATE_REQUEST_SENT - presentation_exchange_record.presentation_request = indy_proof_request - async with self._profile.session() as session: - await presentation_exchange_record.save( - session, reason="create (bound) presentation request" - ) - - return presentation_exchange_record, presentation_request_message - - async def create_exchange_for_request( - self, - connection_id: str, - presentation_request_message: PresentationRequest, - auto_verify: Optional[bool] = None, - auto_remove: Optional[bool] = None, - ): - """Create a presentation exchange record for input presentation request. - - Args: - connection_id: connection identifier - presentation_request_message: presentation request to use in creating - exchange record, extracting indy proof request and thread id - auto_verify: whether to auto-verify presentation exchange - auto_remove: whether to remove this presentation exchange upon completion - Returns: - Presentation exchange record, updated - - """ - if auto_remove is None: - auto_remove = not self._profile.settings.get("preserve_exchange_records") - presentation_exchange_record = V10PresentationExchange( - connection_id=connection_id, - thread_id=presentation_request_message._thread_id, - initiator=V10PresentationExchange.INITIATOR_SELF, - role=V10PresentationExchange.ROLE_VERIFIER, - state=V10PresentationExchange.STATE_REQUEST_SENT, - presentation_request=presentation_request_message.indy_proof_request(), - presentation_request_dict=presentation_request_message, - auto_verify=auto_verify, - trace=(presentation_request_message._trace is not None), - auto_remove=auto_remove, - ) - async with self._profile.session() as session: - await presentation_exchange_record.save( - session, reason="create (free) presentation request" - ) - - return presentation_exchange_record - - async def receive_request( - self, presentation_exchange_record: V10PresentationExchange - ): - """Receive a presentation request. - - Args: - presentation_exchange_record: presentation exchange record with - request to receive - - Returns: - The presentation_exchange_record, updated - - """ - presentation_exchange_record.state = ( - V10PresentationExchange.STATE_REQUEST_RECEIVED - ) - async with self._profile.session() as session: - await presentation_exchange_record.save( - session, reason="receive presentation request" - ) - - return presentation_exchange_record - - async def create_presentation( - self, - presentation_exchange_record: V10PresentationExchange, - requested_credentials: dict, - comment: Optional[str] = None, - ): - """Create a presentation. - - Args: - presentation_exchange_record: Record to update - requested_credentials: Indy formatted requested_credentials - comment: optional human-readable comment - - - Example `requested_credentials` format, mapping proof request referents (uuid) - to wallet referents (cred id): - - :: - - { - "self_attested_attributes": { - "j233ffbc-bd35-49b1-934f-51e083106f6d": "value" - }, - "requested_attributes": { - "6253ffbb-bd35-49b3-934f-46e083106f6c": { - "cred_id": "5bfa40b7-062b-4ae0-a251-a86c87922c0e", - "revealed": true - } - }, - "requested_predicates": { - "bfc8a97d-60d3-4f21-b998-85eeabe5c8c0": { - "cred_id": "5bfa40b7-062b-4ae0-a251-a86c87922c0e" - } - } - } - - Returns: - A tuple (updated presentation exchange record, presentation message) - - """ - indy_handler = IndyPresExchHandler(self._profile) - indy_proof = await indy_handler.return_presentation( - pres_ex_record=presentation_exchange_record, - requested_credentials=requested_credentials, - ) - - presentation_message = Presentation( - comment=comment, - presentations_attach=[ - AttachDecorator.data_base64( - mapping=indy_proof, ident=ATTACH_DECO_IDS[PRESENTATION] - ) - ], - ) - - # Assign thid (and optionally pthid) to message - presentation_message.assign_thread_from( - presentation_exchange_record.presentation_request_dict - ) - presentation_message.assign_trace_decorator( - self._profile.settings, presentation_exchange_record.trace - ) - - # save presentation exchange state - presentation_exchange_record.state = ( - V10PresentationExchange.STATE_PRESENTATION_SENT - ) - presentation_exchange_record.presentation = indy_proof - async with self._profile.session() as session: - await presentation_exchange_record.save(session, reason="create presentation") - - return presentation_exchange_record, presentation_message - - async def receive_presentation( - self, - message: Presentation, - connection_record: Optional[ConnRecord], - oob_record: Optional[OobRecord], - ): - """Receive a presentation, from message in context on manager creation. - - Returns: - presentation exchange record, retrieved and updated - - """ - presentation = message.indy_proof() - - thread_id = message._thread_id - - # Normally we only set the connection_id to None if an oob record is present - # But present proof supports the old-style AIP-1 connectionless exchange that - # bypasses the oob record. So we can't verify if an oob record is associated with - # the exchange because it is possible that there is None - # - # A connectionless proof doesn't have a connection_id, so default to None - # even if there is no oob record. - if connection_record and connection_record.connection_id and not oob_record: - connection_id = connection_record.connection_id - else: - connection_id = None - - async with self._profile.session() as session: - # Find by thread_id and role. Verify connection id later - presentation_exchange_record = ( - await V10PresentationExchange.retrieve_by_tag_filter( - session, - {"thread_id": thread_id}, - { - "role": V10PresentationExchange.ROLE_VERIFIER, - "connection_id": connection_id, - }, - ) - ) - - # Save connection id (if it wasn't already present) - if connection_record: - presentation_exchange_record.connection_id = connection_record.connection_id - - # Check for bait-and-switch in presented attribute values vs. proposal - if presentation_exchange_record.presentation_proposal_dict: - exchange_pres_proposal = ( - presentation_exchange_record.presentation_proposal_dict - ) - presentation_preview = exchange_pres_proposal.presentation_proposal - - proof_req = presentation_exchange_record._presentation_request.ser - for reft, attr_spec in presentation["requested_proof"][ - "revealed_attrs" - ].items(): - name = proof_req["requested_attributes"][reft]["name"] - value = attr_spec["raw"] - if not presentation_preview.has_attr_spec( - cred_def_id=presentation["identifiers"][attr_spec["sub_proof_index"]][ - "cred_def_id" - ], - name=name, - value=value, - ): - presentation_exchange_record.state = ( - V10PresentationExchange.STATE_ABANDONED - ) - async with self._profile.session() as session: - await presentation_exchange_record.save( - session, - reason=( - f"Presentation {name}={value} mismatches proposal value" - ), - ) - raise PresentationManagerError( - f"Presentation {name}={value} mismatches proposal value" - ) - - presentation_exchange_record.presentation = presentation - presentation_exchange_record.state = ( - V10PresentationExchange.STATE_PRESENTATION_RECEIVED - ) - - async with self._profile.session() as session: - await presentation_exchange_record.save( - session, reason="receive presentation" - ) - - return presentation_exchange_record - - async def verify_presentation( - self, - presentation_exchange_record: V10PresentationExchange, - responder: Optional[BaseResponder] = None, - ): - """Verify a presentation. - - Args: - presentation_exchange_record: presentation exchange record - with presentation request and presentation to verify - responder: responder to use - - Returns: - presentation record, updated - - """ - indy_proof_request = presentation_exchange_record._presentation_request.ser - indy_proof = presentation_exchange_record._presentation.ser - indy_handler = IndyPresExchHandler(self._profile) - ( - schemas, - cred_defs, - rev_reg_defs, - rev_reg_entries, - ) = await indy_handler.process_pres_identifiers(indy_proof["identifiers"]) - - verifier = self._profile.inject(IndyVerifier) - (verified_bool, verified_msgs) = await verifier.verify_presentation( - dict( - indy_proof_request - ), # copy to avoid changing the proof req in the stored pres exch - indy_proof, - schemas, - cred_defs, - rev_reg_defs, - rev_reg_entries, - ) - presentation_exchange_record.verified = json.dumps(verified_bool) - presentation_exchange_record.verified_msgs = list(set(verified_msgs)) - presentation_exchange_record.state = V10PresentationExchange.STATE_VERIFIED - - async with self._profile.session() as session: - await presentation_exchange_record.save(session, reason="verify presentation") - - await self.send_presentation_ack(presentation_exchange_record, responder) - return presentation_exchange_record - - async def send_presentation_ack( - self, - presentation_exchange_record: V10PresentationExchange, - responder: Optional[BaseResponder] = None, - ): - """Send acknowledgement of presentation receipt. - - Args: - presentation_exchange_record: presentation exchange record with thread id - responder: Responder to use - - """ - responder = responder or self._profile.inject_or(BaseResponder) - - if not presentation_exchange_record.connection_id: - # Find associated oob record. If this presentation exchange is created - # without oob (aip1 style connectionless) we can't send a presentation ack - # because we don't have their service - try: - async with self._profile.session() as session: - await OobRecord.retrieve_by_tag_filter( - session, - {"attach_thread_id": presentation_exchange_record.thread_id}, - ) - except StorageNotFoundError: - # This can happen in AIP1 style connectionless exchange. ACA-PY only - # supported this for receiving a presentation - LOGGER.error( - "Unable to send connectionless presentation ack without associated " - "oob record. This can happen if proof request was sent without " - "wrapping it in an out of band invitation (AIP1-style)." - ) - return - - if responder: - presentation_ack_message = PresentationAck( - verification_result=presentation_exchange_record.verified - ) - presentation_ack_message._thread = { - "thid": presentation_exchange_record.thread_id - } - presentation_ack_message.assign_trace_decorator( - self._profile.settings, presentation_exchange_record.trace - ) - - await responder.send_reply( - presentation_ack_message, - # connection_id can be none in case of connectionless - connection_id=presentation_exchange_record.connection_id, - ) - - # all done: delete - if presentation_exchange_record.auto_remove: - async with self._profile.session() as session: - await presentation_exchange_record.delete_record(session) - else: - LOGGER.warning( - "Configuration has no BaseResponder: cannot ack presentation on %s", - presentation_exchange_record.thread_id, - ) - - async def receive_presentation_ack( - self, message: PresentationAck, connection_record: Optional[ConnRecord] - ): - """Receive a presentation ack, from message in context on manager creation. - - Returns: - presentation exchange record, retrieved and updated - - """ - connection_id = connection_record.connection_id if connection_record else None - - async with self._profile.session() as session: - ( - presentation_exchange_record - ) = await V10PresentationExchange.retrieve_by_tag_filter( - session, - {"thread_id": message._thread_id}, - { - # connection_id can be null in connectionless - "connection_id": connection_id, - "role": V10PresentationExchange.ROLE_PROVER, - }, - ) - presentation_exchange_record.verified = message._verification_result - presentation_exchange_record.state = ( - V10PresentationExchange.STATE_PRESENTATION_ACKED - ) - - await presentation_exchange_record.save( - session, reason="receive presentation ack" - ) - - # all done: delete - if presentation_exchange_record.auto_remove: - async with self._profile.session() as session: - await presentation_exchange_record.delete_record(session) - - return presentation_exchange_record - - async def receive_problem_report( - self, message: PresentationProblemReport, connection_id: str - ): - """Receive problem report. - - Returns: - presentation exchange record, retrieved and updated - - """ - # FIXME use transaction, fetch for_update - async with self._profile.session() as session: - pres_ex_record = await V10PresentationExchange.retrieve_by_tag_filter( - session, - {"thread_id": message._thread_id}, - {"connection_id": connection_id}, - ) - - pres_ex_record.state = V10PresentationExchange.STATE_ABANDONED - code = message.description.get("code", ProblemReportReason.ABANDONED.value) - pres_ex_record.error_msg = f"{code}: {message.description.get('en', code)}" - await pres_ex_record.save(session, reason="received problem report") - - return pres_ex_record diff --git a/aries_cloudagent/protocols/present_proof/v1_0/message_types.py b/aries_cloudagent/protocols/present_proof/v1_0/message_types.py deleted file mode 100644 index 4ac08981b9..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/message_types.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Message and inner object type identifiers for present-proof protocol v1.0.""" - -from ...didcomm_prefix import DIDCommPrefix - -SPEC_URI = ( - "https://github.com/hyperledger/aries-rfcs/tree/" - "4fae574c03f9f1013db30bf2c0c676b1122f7149/features/0037-present-proof" -) - -# Message types -PRESENTATION_PROPOSAL = "present-proof/1.0/propose-presentation" -PRESENTATION_REQUEST = "present-proof/1.0/request-presentation" -PRESENTATION = "present-proof/1.0/presentation" -PRESENTATION_ACK = "present-proof/1.0/ack" -PRESENTATION_PROBLEM_REPORT = "present-proof/1.0/problem-report" - -PROTOCOL_PACKAGE = "aries_cloudagent.protocols.present_proof.v1_0" - -MESSAGE_TYPES = DIDCommPrefix.qualify_all( - { - PRESENTATION_PROPOSAL: ( - f"{PROTOCOL_PACKAGE}.messages.presentation_proposal.PresentationProposal" - ), - PRESENTATION_REQUEST: ( - f"{PROTOCOL_PACKAGE}.messages.presentation_request.PresentationRequest" - ), - PRESENTATION: f"{PROTOCOL_PACKAGE}.messages.presentation.Presentation", - PRESENTATION_ACK: ( - f"{PROTOCOL_PACKAGE}.messages.presentation_ack.PresentationAck" - ), - PRESENTATION_PROBLEM_REPORT: ( - f"{PROTOCOL_PACKAGE}.messages.presentation_problem_report." - "PresentationProblemReport" - ), - } -) - -# Identifiers to use in attachment decorators -ATTACH_DECO_IDS = { - PRESENTATION_REQUEST: "libindy-request-presentation-0", - PRESENTATION: "libindy-presentation-0", -} - -CONTROLLERS = DIDCommPrefix.qualify_all( - {"present-proof/1.0": f"{PROTOCOL_PACKAGE}.controller.Controller"} -) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/__init__.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation.py deleted file mode 100644 index 9cf84c0190..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation.py +++ /dev/null @@ -1,77 +0,0 @@ -"""A (proof) presentation content message.""" - -from typing import Optional, Sequence - -from marshmallow import EXCLUDE, fields - -from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from .....messaging.decorators.attach_decorator import ( - AttachDecorator, - AttachDecoratorSchema, -) -from ..message_types import PRESENTATION, PROTOCOL_PACKAGE - -HANDLER_CLASS = f"{PROTOCOL_PACKAGE}.handlers.presentation_handler.PresentationHandler" - - -class Presentation(AgentMessage): - """Class representing a (proof) presentation.""" - - class Meta: - """Presentation metadata.""" - - handler_class = HANDLER_CLASS - schema_class = "PresentationSchema" - message_type = PRESENTATION - - def __init__( - self, - _id: Optional[str] = None, - *, - comment: Optional[str] = None, - presentations_attach: Sequence[AttachDecorator] = None, - **kwargs, - ): - """Initialize presentation object. - - Args: - _id (str): The ID of the presentation object. - comment (str, optional): An optional comment. - presentations_attach (Sequence[AttachDecorator], optional): Attachments. - kwargs: Additional keyword arguments for message. - - """ - super().__init__(_id=_id, **kwargs) - self.comment = comment - self.presentations_attach = ( - list(presentations_attach) if presentations_attach else [] - ) - - def indy_proof(self, index: int = 0): - """Retrieve and decode indy proof from attachment. - - Args: - index: ordinal in attachment list to decode and return - (typically, list has length 1) - - """ - return self.presentations_attach[index].content - - -class PresentationSchema(AgentMessageSchema): - """(Proof) presentation schema.""" - - class Meta: - """Presentation schema metadata.""" - - model_class = Presentation - unknown = EXCLUDE - - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - presentations_attach = fields.Nested( - AttachDecoratorSchema, required=True, many=True, data_key="presentations~attach" - ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_ack.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_ack.py deleted file mode 100644 index ebebf9112b..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_ack.py +++ /dev/null @@ -1,59 +0,0 @@ -"""Represents an explicit RFC 15 ack message, adopted into present-proof protocol.""" - -from typing import Optional - -from marshmallow import EXCLUDE, fields, validate - -from ....notification.v1_0.messages.ack import V10Ack, V10AckSchema -from ..message_types import PRESENTATION_ACK, PROTOCOL_PACKAGE - -HANDLER_CLASS = ( - f"{PROTOCOL_PACKAGE}.handlers.presentation_ack_handler.PresentationAckHandler" -) - - -class PresentationAck(V10Ack): - """Base class representing an explicit ack message for present-proof protocol.""" - - class Meta: - """PresentationAck metadata.""" - - handler_class = HANDLER_CLASS - message_type = PRESENTATION_ACK - schema_class = "PresentationAckSchema" - - def __init__( - self, - status: Optional[str] = None, - verification_result: Optional[str] = None, - **kwargs, - ): - """Initialize an explicit ack message instance. - - Args: - status: Status (default OK) - verification_result: Whether presentation is verified - kwargs: Additional keyword arguments for message construction - - """ - super().__init__(status, **kwargs) - self._verification_result = verification_result - - -class PresentationAckSchema(V10AckSchema): - """Schema for PresentationAck class.""" - - class Meta: - """PresentationAck schema metadata.""" - - model_class = PresentationAck - unknown = EXCLUDE - - verification_result = fields.Str( - required=False, - validate=validate.OneOf(["true", "false"]), - metadata={ - "description": "Whether presentation is verified: true or false", - "example": "true", - }, - ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_problem_report.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_problem_report.py deleted file mode 100644 index 558717fe7d..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_problem_report.py +++ /dev/null @@ -1,56 +0,0 @@ -"""A problem report message.""" - -from enum import Enum - -from marshmallow import EXCLUDE, ValidationError, validates_schema - -from ....problem_report.v1_0.message import ProblemReport, ProblemReportSchema -from ..message_types import PRESENTATION_PROBLEM_REPORT, PROTOCOL_PACKAGE - -HANDLER_CLASS = ( - f"{PROTOCOL_PACKAGE}.handlers.presentation_problem_report_handler." - "PresentationProblemReportHandler" -) - - -class ProblemReportReason(Enum): - """Supported reason codes.""" - - ABANDONED = "abandoned" - - -class PresentationProblemReport(ProblemReport): - """Class representing a problem report message.""" - - class Meta: - """Problem report metadata.""" - - handler_class = HANDLER_CLASS - schema_class = "PresentationProblemReportSchema" - message_type = PRESENTATION_PROBLEM_REPORT - - def __init__(self, *args, **kwargs): - """Initialize problem report object.""" - super().__init__(*args, **kwargs) - - -class PresentationProblemReportSchema(ProblemReportSchema): - """Problem report schema.""" - - class Meta: - """Schema metadata.""" - - model_class = PresentationProblemReport - unknown = EXCLUDE - - @validates_schema - def validate_fields(self, data, **kwargs): - """Validate schema fields. - - Args: - data: The data to validate - kwargs: Additional keyword arguments - - """ - if not data.get("description", {}).get("code", ""): - raise ValidationError("Value for description.code must be present") diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_proposal.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_proposal.py deleted file mode 100644 index 31b23b04b5..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_proposal.py +++ /dev/null @@ -1,61 +0,0 @@ -"""A presentation proposal content message.""" - -from typing import Optional - -from marshmallow import EXCLUDE, fields - -from .....indy.models.pres_preview import IndyPresPreview, IndyPresPreviewSchema -from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from ..message_types import PRESENTATION_PROPOSAL, PROTOCOL_PACKAGE - -HANDLER_CLASS = ( - f"{PROTOCOL_PACKAGE}.handlers." - "presentation_proposal_handler.PresentationProposalHandler" -) - - -class PresentationProposal(AgentMessage): - """Class representing a presentation proposal.""" - - class Meta: - """PresentationProposal metadata.""" - - handler_class = HANDLER_CLASS - schema_class = "PresentationProposalSchema" - message_type = PRESENTATION_PROPOSAL - - def __init__( - self, - _id: Optional[str] = None, - *, - comment: Optional[str] = None, - presentation_proposal: Optional[IndyPresPreview] = None, - **kwargs, - ): - """Initialize presentation proposal object. - - Args: - comment: optional human-readable comment - presentation_proposal: proposed presentation preview - kwargs: additional keyword arguments for message - """ - super().__init__(_id, **kwargs) - self.comment = comment - self.presentation_proposal = presentation_proposal - - -class PresentationProposalSchema(AgentMessageSchema): - """Presentation proposal schema.""" - - class Meta: - """Presentation proposal schema metadata.""" - - model_class = PresentationProposal - unknown = EXCLUDE - - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - presentation_proposal = fields.Nested(IndyPresPreviewSchema, required=True) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_request.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_request.py deleted file mode 100644 index 0eb44ca721..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_request.py +++ /dev/null @@ -1,82 +0,0 @@ -"""A presentation request content message.""" - -from typing import Optional, Sequence - -from marshmallow import EXCLUDE, fields - -from .....messaging.agent_message import AgentMessage, AgentMessageSchema -from .....messaging.decorators.attach_decorator import ( - AttachDecorator, - AttachDecoratorSchema, -) -from ..message_types import PRESENTATION_REQUEST, PROTOCOL_PACKAGE - -HANDLER_CLASS = ( - f"{PROTOCOL_PACKAGE}.handlers." - "presentation_request_handler.PresentationRequestHandler" -) - - -class PresentationRequest(AgentMessage): - """Class representing a presentation request.""" - - class Meta: - """PresentationRequest metadata.""" - - handler_class = HANDLER_CLASS - schema_class = "PresentationRequestSchema" - message_type = PRESENTATION_REQUEST - - def __init__( - self, - _id: Optional[str] = None, - *, - comment: Optional[str] = None, - request_presentations_attach: Sequence[AttachDecorator] = None, - **kwargs, - ): - """Initialize presentation request object. - - Args: - comment: optional comment - request_presentations_attach: proof request attachments - kwargs: additional keyword arguments for message - - """ - super().__init__(_id=_id, **kwargs) - self.comment = comment - self.request_presentations_attach = ( - list(request_presentations_attach) if request_presentations_attach else [] - ) - - def indy_proof_request(self, index: int = 0): - """Retrieve and decode indy proof request from attachment. - - Args: - index: ordinal in attachment list to decode and return - (typically, list has length 1) - - """ - return self.request_presentations_attach[index].content - - -class PresentationRequestSchema(AgentMessageSchema): - """Presentation request schema.""" - - class Meta: - """Presentation request schema metadata.""" - - model_class = PresentationRequest - unknown = EXCLUDE - - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - request_presentations_attach = fields.Nested( - AttachDecoratorSchema, - required=True, - many=True, - data_key="request_presentations~attach", - ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_webhook.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_webhook.py deleted file mode 100644 index d0de978ad2..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/presentation_webhook.py +++ /dev/null @@ -1,38 +0,0 @@ -"""v1.0 presentation exchange information webhook.""" - - -class V10PresentationExchangeWebhook: - """Class representing a state only presentation exchange webhook.""" - - __acceptable_keys_list = [ - "connection_id", - "presentation_exchange_id", - "role", - "initiator", - "auto_present", - "auto_verify", - "error_msg", - "state", - "thread_id", - "trace", - "verified", - "verified_msgs", - "created_at", - "updated_at", - ] - - def __init__( - self, - **kwargs, - ): - """Initialize webhook object from V10PresentationExchange. - - from a list of accepted attributes. - """ - [ - self.__setattr__(key, kwargs.get(key)) - for key in self.__acceptable_keys_list - if kwargs.get(key) is not None - ] - if kwargs.get("_id") is not None: - self.presentation_exchange_id = kwargs.get("_id") diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/__init__.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation.py deleted file mode 100644 index 6c4773a2a5..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation.py +++ /dev/null @@ -1,1737 +0,0 @@ -import json -from unittest import TestCase - -from ......messaging.decorators.attach_decorator import AttachDecorator -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import ATTACH_DECO_IDS, PRESENTATION -from ..presentation import Presentation - -INDY_PROOF = json.loads( - """{ - "proof": { - "proofs": [ - { - "primary_proof": { - "eq_proof": { - "revealed_attrs": { - "player": "51643998292319337989293919354395093705917445045137690661130646028663839100479", - "screencapture": "44349549498354774830429200435932754610833874667251545521048906777181334567815" - }, - "a_prime": "99225796363129499107604366233301127916801972855861917994091548556785477066502130364812678473656139160841991495705941663142249404264191136660967090000331013804872112053828446231809361437830836319053659748356431221803865514426935793809384997872056337751830616632363564742453553492463002290910985263848243042219992778220569301291770422529015543837803240796109915443060533925706334481433988647943330126921627882396088865869804752372160284361135101444121353171684921889674279086653358367368851378746682682017641177839566946615542088910003479771742848013401738739436319413416417782857315505655723403098381845469564775640588", - "e": "28484172708495089688591061270972815967199639781170588390863001534745829714460906432474939589201651469315129053056279820725958192110265136", - "v": "310372334186966867767394517648718214703060542831249429833856393387373123821840122943078768258679350688701210557571314095023780969910990133962277141702540794078947706642651608840502392669887782435197020314627659897216201821476693212776945691916847975724720629133302982522740709659244048850715861867163370042548490158397753134853555833520807793752332629616695158900467048806794540963892785661237500652426649694476839734543710070772542960644069106447786837388953205349573770161012926822761642443603379863014577283739370081232865330663720633954578384601051328768426422459925307605555673543912329275856396630694738618400128412881389666175048976575778590587039239852074579445246476657508798666439893978860122625600280248580653651989659501745788120866210838204610848826992305622269021702688221635993649651671518759866100294588385482901147670474709698187899410921387549396343476268788611619095756118794378610337782521199137176224", - "m": { - "master_secret": "8862971523696585539875886113995946020345090415446970983664333029999473510798222518918685777004680817221644138616821331322893963179678008732473561080506239631376575074759262558623", - "date": "3701779401056653400708681878780576462168055130242160156441633682389568986593680911678649493653787250169881692457012639607423195648009201693522087171287177627155679953691027082306", - "highscore": "15076065627409123507707791591890677721352422176962229116158012124884023896353283613850809563416017162039356935197216493911366484372240599638993754251972383037120760793174059437326" - }, - "m2": "936898843995229611075174877423066852536402039331414286329629096155063110397949209326899164087270236968111471019540493930568502892781443118611948331343540849982215419978654911341" - }, - "ge_proofs": [ - { - "u": { - "0": "9910469266382558421810537687107445527637953525140204243652090909154732881567346670639902692019649848585497930780041894066589111086262231121289584890680947709857922351898933228959", - "3": "13248890365144372967021124637790988823123419165600968402954657790395188046865908780216014168108873473963822724485182321396055154711186623889234974568160016086782335901983921278203", - "2": "12729039529929764954731327277162243472469670773258016331674525566138793186295239771259296208473089652983817249211287815365374343774154094615763169572305994728783319085378462750119", - "1": "7521808223922555179229230989469494924108464850902024304215849946306721494292541804707880060117792628557556919883251581183099791703469635100219991762282695219119375485542725378777" - }, - "r": { - "3": "2474586272027077975486776866285873096434331606893837372003598899080539007560599606386516216782289932402894639261205313757395792446618729476125758006073556965534129613180311177356435264610207048503766208536525437467431668179066975773784256747360733829457728689042564760785075167221871190381905962993342585054474809475874638461649882224522900216073005325070483781773167488104736004488166472769233964211119250710987817940295641154170092028642853085492684423831557787832223441677166939601580910358630679766277360055054583280321123214001754046780045473183143311899432032961118915743585200408079711683568075159956637355186537460890150634531127711285758617739650166494804700747238587328374163718880396805711619195410428497141", - "2": "2630464840206472451352435662547628615461823248193121896366292326754757111056015434024860402045528167154717793472610145065612236007297496233371229987771148480914236050468139151516543123252130359806069866913832582652430060368351667452390252792745981559902564451307173881741056494603759634524628446420629554945618261322035719400890137143004894938649567505283955045583301734843724484105958980144825603458470170246633173176192352102370358808823038609216670566297573459331481693366189829604088382174720921421068848195803685053584587847959340545747151323994860573252761484349482452752365951814578536977631851802458952874933908594142054875532155473403049377997857193944575096457437636198069049894085647451273888200116980726092", - "1": "2852147521010832076474693080123829749829373205563299890275783906381127244731842078098806253403603591226341534169437752738669698923225573040124923814326088208465858997309773274462266090025447286378141544917213418789777276232863321772419735930833747576309139155217894968446024099207333956546610641531588126922714769703447074214896402884035961312686767119156707888839495093047502240940442068243444839642678428392561564279122033304060367830470731800699885137708112213347900071682836322404659581146632750296228233441035302852186755012460856485782729749727931571925009194110383907166489891246153746477910501305713189452876479941940283249570571466801547498554092112942172290619708436101630604721002777991653223187127539407188", - "DELTA": "400740231805932179607546658608482360416676808879808936308239007450069335712770990135423875914299915061808733825416765503161922435087393607455279098108543704733230814698288332881292132566532892722244536550609474863487095816391676106247864333163126795882262678039103218492008333619274792818770308974444039810096709828122153085809072205039719201560334210985909087337968918296450456759914221258287823859138473869581326860149282690703526479416994879663317413415525469689392534867388970645182739614666457086788145724809368914878257774143699515974528212285813531498884015621850779340589427600835454594927635608618313963836648119837777098673007860656489994343544396208432731266075365830717274498351940211946906749568641992530", - "0": "1206673881851533752176657850353675358524597024445357836801291763123272463247544653871603547107824681844497100741157733091042299879547696954660696997520368168483474593036101472335505287047339386308031509611499543209773577503155192535635651933608157610580443175876534879594575874473220014224237470499919793664212944782077926675612730961243351448995290239215801924035454011696132815884654568365382507261689165029962957712345451405751438882798844088168256631131921905245510548989991506332080163507201398283921938862585730222296508424960186566696340473016767188656883762864118588802209468135703456208025238541839477324582906436589408122727413989766360283059475263178640070468528674228956264205722590748705114610224502937924" - }, - "mj": "15076065627409123507707791591890677721352422176962229116158012124884023896353283613850809563416017162039356935197216493911366484372240599638993754251972383037120760793174059437326", - "alpha": "20251550018805200717806858447687954659786798446794929315180340450717009476769405863150379133594211911561358797900043949141708879953965949034296837455168571993614131838308136400934080334484858045221098438795729643169952299593947544536931262318894249728605957882845005603393308631544441292352568325855333088710935761954865297018529190379824999153478968957974616452369432737206876894394433169704416574734350248494633324787866283669843201013454433040804437117653085130836624250598443032846839559239219803003053865279821640383428381442135797658167010830469471880710203270574788431679668220274396595963367127851323648855427656787139315767612273110920619973147968805075620184678295428649237076408491062282135768652643652528789723106929481799840238867321416457406628403722479816549749574895345371486180196838697381621782729034821539642473730374275", - "t": { - "1": "12625197327271658918836077104165526795551240746110291943686378253405709448817799744491714171274845559259436160572533367386665079411321345166308961745117961730852997405633497191717007336383275404469818669156913360917193297281721819431590624901667128875332561566036439988983652391578753211775620012967251966145029999420901103522072647380944582775843791262702644151927742979273959780439648875773579687106661837930590989533046533664173215551687012232903455587682542013722715620746003808218596250895466798585940038977660031964518043170383195848432855521396949050006496669882466103602834555814104353098012178481563132990657", - "3": "82102416726449754901570630901431660447826687124743534767954749884467633032358726226619062293813250820543583618667653110864397826099702636976514863698490371598871942970264169528954417033219855319798151022602756245903134559243361308006137131575819330897670063637536795053765101329851925607560890238602738686737347630399680932950512292412006361269539738453753560364596561872651528860308101942007770489206306048924418921104517753483478955863296623417733412161191192531054326372049247205543273207371278781809399097610512792780914259992762072456575639120070897889219135350947197581287043954055372025101673838669553746551523", - "0": "100578099981822727242488292109669009229478055276500695778799086886344998432604032811665840061704724353178176792298171825200217745276011656576161627798148614876492383276153655146449848780838571509873143828996025628954667317519574656744701630828190045526936155193536814016169445565475181479441229047491855276823646742587245970832496856994388840793376871874193330364608394771574962996229647270622689890201908589893313568444474914909794303851820492781326574727803226373005399197371492982012783800353741451399606551384719595965296619783050926116585174881782168129321205830465290478768408675156580724359333089093105010344487", - "2": "47291536708088381287407033267847414228876334422991232636387475485756328314399598367105968385520172836890544717976118198568671113811836108861048793780118048683411340116566023370245246884524520199561342298868861751758445312599348599287067000725278934840752177807977101054892905295530294108292736307777321970970868898458355273485795649568677223443447768573057466329236959267653001983213430774265365847091875699626385937604178216275273379502023024485339694410370916685404579472512288185963724377525685276628144678139522579811749896221643038522340842472046618109166452353106698715375908659582424315255951960930185079622552", - "DELTA": "55673614276503115042406892194681370272903807098038274960776275804979087176140123726613332530447421097732347173352956738522605590407126570163366084735258393133886870700490345929950624260625461471012084011187108973815830590105522983606251371051538463584013547099942110852482167674597067842508689609606420417081221833855564428834462819662758502495039815615824926366319292041564418283778981490957086445486745581161189382261760754225714728934548296947403634289640526526314947616964122321833465956469712078741533550908164428460947933509296796422321858634999992086190358241952920458802129165732538146634862846975496258789679" - }, - "predicate": { - "attr_name": "highscore", - "p_type": "GE", - "value": 1000000 - } - } - ] - }, - "non_revoc_proof": null - } - ], - "aggregated_proof": { - "c_hash": "81147637626301581116624461636159287563986704950981430016774756525127013830996", - "c_list": [ - [ - 3, - 18, - 5, - 11, - 249, - 192, - 147, - 232, - 208, - 2, - 120, - 15, - 246, - 67, - 152, - 178, - 13, - 223, - 45, - 197, - 49, - 251, - 124, - 129, - 88, - 30, - 22, - 215, - 93, - 198, - 188, - 111, - 134, - 78, - 237, - 244, - 150, - 57, - 134, - 207, - 48, - 252, - 238, - 215, - 44, - 69, - 28, - 38, - 231, - 95, - 66, - 222, - 118, - 30, - 137, - 6, - 78, - 103, - 185, - 218, - 139, - 176, - 149, - 97, - 40, - 224, - 246, - 241, - 87, - 80, - 58, - 169, - 185, - 39, - 121, - 175, - 175, - 181, - 73, - 172, - 152, - 149, - 252, - 2, - 237, - 255, - 147, - 215, - 212, - 0, - 134, - 24, - 198, - 1, - 241, - 191, - 206, - 227, - 200, - 228, - 32, - 22, - 90, - 101, - 237, - 161, - 32, - 157, - 211, - 231, - 28, - 106, - 42, - 227, - 234, - 207, - 116, - 119, - 121, - 173, - 188, - 167, - 195, - 218, - 223, - 194, - 123, - 102, - 140, - 36, - 121, - 231, - 254, - 240, - 155, - 55, - 244, - 236, - 106, - 84, - 62, - 169, - 69, - 56, - 191, - 61, - 29, - 29, - 117, - 196, - 40, - 26, - 210, - 204, - 194, - 164, - 5, - 25, - 138, - 235, - 164, - 176, - 182, - 32, - 100, - 24, - 52, - 71, - 227, - 199, - 45, - 162, - 88, - 66, - 245, - 222, - 51, - 250, - 174, - 222, - 34, - 93, - 63, - 181, - 49, - 45, - 226, - 120, - 183, - 81, - 127, - 222, - 168, - 100, - 99, - 8, - 8, - 248, - 24, - 142, - 118, - 99, - 42, - 157, - 170, - 117, - 103, - 183, - 22, - 253, - 189, - 186, - 234, - 88, - 129, - 202, - 193, - 32, - 237, - 49, - 251, - 49, - 131, - 183, - 2, - 22, - 44, - 207, - 13, - 83, - 98, - 38, - 14, - 160, - 14, - 13, - 146, - 108, - 239, - 43, - 47, - 238, - 251, - 17, - 206, - 164, - 179, - 185, - 103, - 219, - 80, - 159, - 145, - 184, - 239, - 46, - 12 - ], - [ - 3, - 28, - 187, - 101, - 204, - 218, - 140, - 64, - 119, - 109, - 189, - 77, - 133, - 186, - 157, - 230, - 147, - 59, - 219, - 42, - 64, - 16, - 163, - 132, - 197, - 115, - 236, - 3, - 117, - 211, - 98, - 142, - 33, - 166, - 85, - 1, - 88, - 93, - 245, - 55, - 253, - 248, - 59, - 240, - 70, - 169, - 206, - 15, - 157, - 202, - 59, - 254, - 204, - 251, - 3, - 126, - 139, - 138, - 251, - 103, - 229, - 185, - 66, - 105, - 188, - 36, - 47, - 233, - 32, - 148, - 14, - 116, - 14, - 40, - 62, - 209, - 131, - 62, - 108, - 124, - 251, - 157, - 114, - 208, - 94, - 195, - 239, - 168, - 196, - 162, - 19, - 23, - 21, - 215, - 235, - 26, - 12, - 211, - 250, - 184, - 14, - 57, - 116, - 53, - 94, - 179, - 92, - 6, - 45, - 72, - 140, - 173, - 133, - 162, - 150, - 17, - 235, - 31, - 82, - 88, - 14, - 89, - 143, - 166, - 97, - 157, - 250, - 191, - 236, - 95, - 115, - 137, - 102, - 29, - 61, - 179, - 40, - 219, - 182, - 124, - 162, - 134, - 146, - 113, - 137, - 234, - 30, - 130, - 201, - 215, - 22, - 28, - 40, - 108, - 174, - 166, - 191, - 239, - 251, - 166, - 163, - 248, - 245, - 140, - 249, - 199, - 168, - 137, - 50, - 230, - 83, - 204, - 238, - 235, - 156, - 202, - 77, - 1, - 12, - 112, - 242, - 56, - 189, - 100, - 37, - 43, - 139, - 230, - 60, - 235, - 94, - 110, - 13, - 51, - 230, - 136, - 33, - 208, - 191, - 83, - 149, - 167, - 17, - 255, - 252, - 115, - 11, - 177, - 12, - 98, - 208, - 13, - 82, - 83, - 78, - 81, - 44, - 77, - 166, - 235, - 230, - 94, - 52, - 76, - 191, - 176, - 18, - 64, - 223, - 96, - 145, - 51, - 38, - 236, - 143, - 134, - 22, - 244, - 116, - 214, - 26, - 66, - 199, - 249, - 64, - 11, - 164, - 153, - 174, - 107, - 201, - 247, - 134, - 223, - 136, - 2, - 39 - ], - [ - 100, - 2, - 197, - 149, - 94, - 78, - 16, - 15, - 216, - 212, - 33, - 205, - 178, - 90, - 159, - 110, - 12, - 9, - 195, - 172, - 98, - 84, - 106, - 166, - 143, - 8, - 199, - 177, - 41, - 127, - 219, - 144, - 203, - 178, - 101, - 82, - 112, - 39, - 1, - 201, - 198, - 130, - 88, - 22, - 198, - 20, - 169, - 14, - 201, - 230, - 67, - 228, - 169, - 137, - 134, - 157, - 105, - 111, - 4, - 85, - 56, - 183, - 107, - 8, - 1, - 230, - 16, - 54, - 137, - 81, - 99, - 165, - 2, - 191, - 84, - 188, - 68, - 200, - 91, - 223, - 145, - 201, - 36, - 217, - 23, - 124, - 88, - 78, - 186, - 186, - 63, - 25, - 188, - 95, - 138, - 240, - 187, - 154, - 27, - 12, - 228, - 173, - 156, - 225, - 43, - 200, - 163, - 221, - 241, - 105, - 61, - 99, - 182, - 150, - 56, - 141, - 248, - 113, - 54, - 231, - 19, - 51, - 4, - 232, - 15, - 70, - 213, - 186, - 10, - 247, - 219, - 255, - 159, - 30, - 42, - 205, - 228, - 91, - 1, - 158, - 90, - 6, - 112, - 252, - 153, - 234, - 57, - 90, - 107, - 172, - 180, - 150, - 189, - 188, - 201, - 143, - 121, - 38, - 51, - 235, - 122, - 163, - 129, - 205, - 24, - 30, - 59, - 91, - 233, - 1, - 80, - 186, - 199, - 153, - 222, - 201, - 78, - 156, - 74, - 111, - 31, - 105, - 83, - 23, - 167, - 55, - 2, - 38, - 102, - 254, - 51, - 157, - 37, - 83, - 232, - 48, - 29, - 108, - 30, - 13, - 152, - 151, - 27, - 218, - 2, - 59, - 4, - 74, - 22, - 127, - 186, - 54, - 120, - 127, - 203, - 250, - 161, - 6, - 9, - 166, - 122, - 112, - 141, - 64, - 60, - 192, - 95, - 47, - 191, - 8, - 94, - 231, - 5, - 11, - 61, - 239, - 136, - 85, - 56, - 42, - 11, - 224, - 60, - 229, - 139, - 244, - 25, - 26, - 159, - 166, - 79, - 67, - 12, - 111, - 148, - 193 - ], - [ - 1, - 118, - 159, - 2, - 129, - 184, - 137, - 5, - 51, - 164, - 24, - 85, - 155, - 119, - 100, - 109, - 91, - 14, - 209, - 217, - 55, - 243, - 140, - 157, - 24, - 70, - 85, - 43, - 5, - 8, - 112, - 215, - 228, - 90, - 166, - 205, - 46, - 79, - 107, - 162, - 136, - 139, - 7, - 34, - 80, - 253, - 216, - 178, - 107, - 67, - 44, - 184, - 135, - 90, - 140, - 117, - 10, - 237, - 33, - 146, - 73, - 88, - 123, - 61, - 203, - 227, - 138, - 96, - 130, - 148, - 4, - 70, - 34, - 234, - 229, - 13, - 25, - 202, - 122, - 58, - 244, - 228, - 234, - 223, - 237, - 124, - 22, - 222, - 229, - 79, - 223, - 138, - 52, - 50, - 28, - 168, - 4, - 214, - 26, - 111, - 217, - 22, - 205, - 149, - 100, - 36, - 40, - 42, - 248, - 58, - 10, - 35, - 103, - 175, - 77, - 175, - 198, - 195, - 122, - 176, - 250, - 57, - 64, - 233, - 128, - 200, - 162, - 124, - 129, - 200, - 54, - 99, - 99, - 237, - 246, - 107, - 97, - 196, - 62, - 167, - 109, - 187, - 143, - 106, - 43, - 133, - 219, - 70, - 181, - 42, - 107, - 13, - 12, - 146, - 149, - 22, - 234, - 39, - 69, - 126, - 128, - 174, - 121, - 208, - 84, - 98, - 130, - 153, - 17, - 20, - 239, - 13, - 190, - 143, - 247, - 160, - 214, - 157, - 53, - 196, - 181, - 181, - 187, - 175, - 76, - 97, - 142, - 193, - 183, - 80, - 88, - 109, - 73, - 178, - 79, - 222, - 47, - 193, - 232, - 233, - 110, - 215, - 229, - 80, - 49, - 145, - 59, - 202, - 136, - 50, - 49, - 12, - 253, - 21, - 122, - 80, - 183, - 142, - 34, - 141, - 237, - 142, - 23, - 99, - 69, - 231, - 105, - 76, - 248, - 237, - 130, - 200, - 215, - 160, - 59, - 25, - 198, - 105, - 130, - 20, - 96, - 200, - 183, - 159, - 232, - 177, - 244, - 84, - 169, - 245, - 209, - 111, - 53, - 240, - 123, - 11, - 152 - ], - [ - 2, - 138, - 96, - 92, - 255, - 34, - 116, - 173, - 20, - 69, - 199, - 3, - 5, - 92, - 201, - 32, - 201, - 31, - 179, - 150, - 90, - 107, - 31, - 3, - 191, - 223, - 78, - 115, - 65, - 64, - 16, - 87, - 247, - 247, - 21, - 69, - 196, - 57, - 136, - 39, - 234, - 158, - 1, - 163, - 252, - 36, - 57, - 107, - 168, - 117, - 225, - 98, - 29, - 146, - 235, - 106, - 133, - 38, - 101, - 9, - 184, - 149, - 75, - 179, - 75, - 156, - 5, - 109, - 37, - 180, - 150, - 97, - 61, - 70, - 97, - 32, - 135, - 82, - 71, - 4, - 200, - 150, - 253, - 125, - 232, - 119, - 231, - 74, - 221, - 185, - 139, - 56, - 214, - 209, - 46, - 138, - 92, - 102, - 93, - 249, - 240, - 97, - 245, - 177, - 115, - 108, - 189, - 68, - 93, - 85, - 108, - 216, - 40, - 161, - 55, - 32, - 13, - 34, - 12, - 198, - 184, - 69, - 10, - 191, - 38, - 79, - 194, - 167, - 19, - 135, - 195, - 62, - 245, - 248, - 122, - 144, - 132, - 233, - 238, - 78, - 242, - 137, - 129, - 117, - 210, - 244, - 53, - 87, - 73, - 246, - 30, - 223, - 83, - 0, - 84, - 83, - 36, - 211, - 231, - 24, - 60, - 58, - 114, - 223, - 218, - 47, - 32, - 47, - 34, - 227, - 224, - 122, - 50, - 215, - 242, - 198, - 104, - 205, - 192, - 11, - 142, - 139, - 17, - 101, - 236, - 88, - 9, - 119, - 137, - 218, - 215, - 73, - 235, - 183, - 59, - 223, - 42, - 203, - 218, - 76, - 184, - 27, - 70, - 225, - 6, - 151, - 2, - 183, - 106, - 124, - 14, - 219, - 58, - 71, - 100, - 2, - 135, - 124, - 43, - 178, - 12, - 140, - 45, - 136, - 135, - 69, - 195, - 219, - 63, - 249, - 58, - 140, - 198, - 123, - 143, - 203, - 132, - 105, - 55, - 36, - 14, - 107, - 211, - 251, - 173, - 102, - 241, - 193, - 165, - 3, - 168, - 108, - 93, - 127, - 3, - 162, - 227 - ], - [ - 1, - 185, - 5, - 29, - 44, - 82, - 241, - 206, - 149, - 5, - 122, - 252, - 235, - 120, - 16, - 15, - 71, - 16, - 151, - 103, - 254, - 245, - 217, - 73, - 207, - 230, - 48, - 243, - 78, - 241, - 168, - 104, - 15, - 36, - 251, - 86, - 253, - 17, - 224, - 55, - 55, - 167, - 239, - 241, - 16, - 62, - 0, - 100, - 53, - 9, - 36, - 151, - 215, - 143, - 218, - 214, - 72, - 24, - 152, - 42, - 144, - 168, - 100, - 122, - 101, - 248, - 55, - 109, - 225, - 78, - 58, - 108, - 185, - 206, - 44, - 23, - 114, - 116, - 222, - 91, - 168, - 112, - 48, - 141, - 64, - 71, - 142, - 191, - 255, - 83, - 126, - 61, - 160, - 123, - 215, - 116, - 45, - 198, - 122, - 62, - 63, - 107, - 40, - 58, - 56, - 166, - 148, - 204, - 220, - 10, - 67, - 200, - 94, - 140, - 173, - 98, - 26, - 61, - 146, - 74, - 106, - 73, - 162, - 150, - 210, - 96, - 244, - 191, - 80, - 109, - 153, - 157, - 59, - 31, - 151, - 218, - 156, - 244, - 212, - 208, - 160, - 112, - 220, - 134, - 64, - 28, - 164, - 111, - 219, - 198, - 234, - 130, - 54, - 20, - 217, - 56, - 115, - 0, - 28, - 44, - 18, - 3, - 8, - 70, - 248, - 157, - 67, - 198, - 216, - 69, - 232, - 236, - 111, - 145, - 191, - 214, - 186, - 208, - 126, - 133, - 151, - 166, - 251, - 30, - 26, - 163, - 255, - 234, - 241, - 251, - 253, - 132, - 247, - 204, - 95, - 124, - 142, - 76, - 250, - 115, - 91, - 240, - 169, - 203, - 162, - 57, - 41, - 42, - 150, - 242, - 72, - 227, - 223, - 76, - 149, - 87, - 153, - 77, - 193, - 63, - 159, - 32, - 190, - 32, - 126, - 53, - 26, - 99, - 95, - 59, - 205, - 22, - 161, - 9, - 195, - 16, - 48, - 79, - 53, - 235, - 46, - 71, - 0, - 8, - 57, - 55, - 6, - 87, - 1, - 198, - 107, - 255, - 135, - 80, - 239, - 33, - 47 - ] - ] - } - }, - "requested_proof": { - "revealed_attrs": { - "0_player_uuid": { - "sub_proof_index": 0, - "raw": "Richie Knucklez", - "encoded": "51643998292319337989293919354395093705917445045137690661130646028663839100479" - }, - "0_screencapture_uuid": { - "sub_proof_index": 0, - "raw": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - "encoded": "44349549498354774830429200435932754610833874667251545521048906777181334567815" - } - }, - "self_attested_attrs": {}, - "unrevealed_attrs": {}, - "predicates": { - "0_highscore_GE_uuid": { - "sub_proof_index": 0 - } - } - }, - "identifiers": [ - { - "schema_id": "WjFgAM9qFept242HWzUSTZ:2:high_score:1.0", - "cred_def_id": "WjFgAM9qFept242HWzUSTZ:3:CL:13:tag", - "rev_reg_id": null, - "timestamp": null - } - ] - }""" -) - -PRES = Presentation( - comment="Test", - presentations_attach=[ - AttachDecorator.data_base64( - mapping=INDY_PROOF, - ident=ATTACH_DECO_IDS[PRESENTATION], - ) - ], -) - - -class TestPresentation(TestCase): - """Presentation tests.""" - - def test_init(self): - """Test initializer.""" - assert PRES.presentations_attach[0].content == INDY_PROOF - assert PRES.indy_proof(0) == INDY_PROOF - - def test_type(self): - """Test type.""" - assert PRES._type == DIDCommPrefix.qualify_current(PRESENTATION) - - def test_deserialize(self): - """Test deserialization.""" - dump = json.dumps( - { - "@type": DIDCommPrefix.qualify_current(PRESENTATION), - "comment": "Hello World", - "presentations~attach": [ - AttachDecorator.data_base64( - mapping=INDY_PROOF, - ident=ATTACH_DECO_IDS[PRESENTATION], - ).serialize() - ], - } - ) - - presentation = Presentation.deserialize(dump) - assert type(presentation) is Presentation - - def test_serialize(self): - """Test serialization.""" - pres_dict = PRES.serialize() - pres_dict.pop("@id") - - assert pres_dict == { - "@type": DIDCommPrefix.qualify_current(PRESENTATION), - "presentations~attach": [ - AttachDecorator.data_base64( - mapping=INDY_PROOF, - ident=ATTACH_DECO_IDS[PRESENTATION], - ).serialize() - ], - "comment": "Test", - } - - -class TestPresentationSchema(TestCase): - """Test presentation schema""" - - def test_make_model(self): - """Test making model.""" - pres_dict = PRES.serialize() - """ - Looks like: { - "@type": ".../present-proof/1.0/presentation", - "@id": "f49773e3-bd56-4868-a5f1-456d1e6d1a16", - "comment": "Test", - "presentations~attach": [ - { - "mime-type": "application/json", - "data": { - "base64": "eyJuYW..." - } - } - ] - } - """ - - model_instance = PRES.deserialize(pres_dict) - assert isinstance(model_instance, Presentation) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_ack.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_ack.py deleted file mode 100644 index 4ad74604c2..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_ack.py +++ /dev/null @@ -1,57 +0,0 @@ -import json -from unittest import TestCase - -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import PRESENTATION_ACK -from ..presentation_ack import PresentationAck - - -class TestPresentationAck(TestCase): - """Presentation ack tests.""" - - def test_init(self): - """Test initializer.""" - pres_ack = PresentationAck() - assert pres_ack.status == "OK" - - def test_type(self): - """Test type.""" - pres_ack = PresentationAck() - assert pres_ack._type == DIDCommPrefix.qualify_current(PRESENTATION_ACK) - - def test_deserialize(self): - """Test deserialization.""" - dump = json.dumps( - {"@type": DIDCommPrefix.qualify_current(PRESENTATION_ACK), "status": "OK"} - ) - - pres_ack = PresentationAck.deserialize(dump) - assert type(pres_ack) is PresentationAck - - def test_serialize(self): - """Test serialization.""" - pres_ack_dict = PresentationAck().serialize() - pres_ack_dict.pop("@id") - - assert pres_ack_dict == { - "@type": DIDCommPrefix.qualify_current(PRESENTATION_ACK), - "status": "OK", - } - - -class TestPresentationAckSchema(TestCase): - """Test presentation ack schema""" - - def test_make_model(self): - """Test making model.""" - pres_ack_dict = PresentationAck().serialize() - """ - Looks like: { - "@type": ".../present-proof/1.0/ack", - "@id": "f49773e3-bd56-4868-a5f1-456d1e6d1a16", - "status": "OK" - } - """ - - model_instance = PresentationAck.deserialize(pres_ack_dict) - assert isinstance(model_instance, PresentationAck) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_problem_report.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_problem_report.py deleted file mode 100644 index 15b4ba8054..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_problem_report.py +++ /dev/null @@ -1,79 +0,0 @@ -from unittest import TestCase, mock - -import pytest - -from ......messaging.models.base import BaseModelError -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import PRESENTATION_PROBLEM_REPORT, PROTOCOL_PACKAGE -from ..presentation_problem_report import PresentationProblemReport, ProblemReportReason - - -class TestPresentationProblemReport(TestCase): - """Problem report tests.""" - - def test_init_type(self): - """Test initializer.""" - - prob = PresentationProblemReport( - description={ - "en": "oh no", - "code": ProblemReportReason.ABANDONED.value, - } - ) - assert prob._type == DIDCommPrefix.qualify_current(PRESENTATION_PROBLEM_REPORT) - - @mock.patch( - f"{PROTOCOL_PACKAGE}.messages.presentation_problem_report." - "PresentationProblemReportSchema.load" - ) - def test_deserialize(self, mock_load): - """Test deserialization.""" - - obj = PresentationProblemReport( - description={ - "en": "oh no", - "code": ProblemReportReason.ABANDONED.value, - } - ) - - prob = PresentationProblemReport.deserialize(obj) - mock_load.assert_called_once_with(obj) - - assert prob is mock_load.return_value - - @mock.patch( - f"{PROTOCOL_PACKAGE}.messages.presentation_problem_report." - "PresentationProblemReportSchema.dump" - ) - def test_serialize(self, mock_dump): - """Test serialization.""" - - obj = PresentationProblemReport( - description={ - "en": "oh no", - "code": ProblemReportReason.ABANDONED.value, - } - ) - - ser = obj.serialize() - mock_dump.assert_called_once_with(obj) - - assert ser is mock_dump.return_value - - def test_make_model(self): - """Test making model.""" - - prob = PresentationProblemReport( - description={ - "en": "oh no", - "code": ProblemReportReason.ABANDONED.value, - } - ) - data = prob.serialize() - model_instance = PresentationProblemReport.deserialize(data) - assert isinstance(model_instance, PresentationProblemReport) - - prob = PresentationProblemReport() - data = prob.serialize() - with pytest.raises(BaseModelError): - PresentationProblemReport.deserialize(data) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_proposal.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_proposal.py deleted file mode 100644 index 42b6e6986c..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_proposal.py +++ /dev/null @@ -1,90 +0,0 @@ -from unittest import TestCase - -from ......indy.models.pres_preview import ( - IndyPresAttrSpec, - IndyPresPredSpec, - IndyPresPreview, -) -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import PRESENTATION_PROPOSAL -from ..presentation_proposal import PresentationProposal - -S_ID = "NcYxiDXkpYi6ov5FcYDi1e:2:vidya:1.0" -CD_ID = f"NcYxiDXkpYi6ov5FcYDi1e:3:CL:{S_ID}:tag1" -PRES_PREVIEW = IndyPresPreview( - attributes=[ - IndyPresAttrSpec(name="player", cred_def_id=CD_ID, value="Richie Knucklez"), - IndyPresAttrSpec( - name="screenCapture", - cred_def_id=CD_ID, - mime_type="image/png", - value="aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - ), - ], - predicates=[ - IndyPresPredSpec( - name="highScore", cred_def_id=CD_ID, predicate=">=", threshold=1000000 - ) - ], -) - - -class TestPresentationProposal(TestCase): - """Presentation proposal tests.""" - - def test_init(self): - """Test initializer.""" - presentation_proposal = PresentationProposal( - comment="Hello World", presentation_proposal=PRES_PREVIEW - ) - assert presentation_proposal.presentation_proposal == PRES_PREVIEW - - def test_type(self): - """Test type.""" - presentation_proposal = PresentationProposal( - comment="Hello World", presentation_proposal=PRES_PREVIEW - ) - assert presentation_proposal._type == DIDCommPrefix.qualify_current( - PRESENTATION_PROPOSAL - ) - - def test_deserialize(self): - """Test deserialization.""" - obj = { - "@type": DIDCommPrefix.qualify_current(PRESENTATION_PROPOSAL), - "comment": "Hello World", - "presentation_proposal": PRES_PREVIEW.serialize(), - } - - pres_proposal = PresentationProposal.deserialize(obj) - assert type(pres_proposal) is PresentationProposal - - def test_serialize(self): - """Test serialization.""" - - pres_proposal = PresentationProposal( - comment="Hello World", presentation_proposal=PRES_PREVIEW - ) - - pres_proposal_dict = pres_proposal.serialize() - pres_proposal_dict.pop("@id") - - assert pres_proposal_dict == { - "@type": DIDCommPrefix.qualify_current(PRESENTATION_PROPOSAL), - "comment": "Hello World", - "presentation_proposal": PRES_PREVIEW.serialize(), - } - - -class TestPresentationProposalSchema(TestCase): - """Test presentation cred proposal schema.""" - - presentation_proposal = PresentationProposal( - comment="Hello World", presentation_proposal=PRES_PREVIEW - ) - - def test_make_model(self): - """Test making model.""" - data = self.presentation_proposal.serialize() - model_instance = PresentationProposal.deserialize(data) - assert isinstance(model_instance, PresentationProposal) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_request.py b/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_request.py deleted file mode 100644 index cdab99499e..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/messages/tests/test_presentation_request.py +++ /dev/null @@ -1,145 +0,0 @@ -import json -from datetime import datetime, timezone -from unittest import TestCase - -from ......messaging.decorators.attach_decorator import AttachDecorator -from ......messaging.util import str_to_epoch -from .....didcomm_prefix import DIDCommPrefix -from ...message_types import ATTACH_DECO_IDS, PRESENTATION_REQUEST -from ..presentation_request import PresentationRequest - -NOW_8601 = datetime.now(tz=timezone.utc).isoformat(" ", "seconds") -NOW_EPOCH = str_to_epoch(NOW_8601) -CD_ID = "GMm4vMw8LLrLJjp81kRRLp:3:CL:12:tag" -INDY_PROOF_REQ = json.loads( - f"""{{ - "name": "proof-req", - "version": "1.0", - "nonce": "12345", - "requested_attributes": {{ - "0_player_uuid": {{ - "name": "player", - "restrictions": [ - {{ - "cred_def_id": "{CD_ID}" - }} - ], - "non_revoked": {{ - "from": {NOW_EPOCH}, - "to": {NOW_EPOCH} - }} - }}, - "0_screencapture_uuid": {{ - "name": "screenCapture", - "restrictions": [ - {{ - "cred_def_id": "{CD_ID}" - }} - ], - "non_revoked": {{ - "from": {NOW_EPOCH}, - "to": {NOW_EPOCH} - }} - }} - }}, - "requested_predicates": {{ - "0_highscore_GE_uuid": {{ - "name": "highScore", - "p_type": ">=", - "p_value": 1000000, - "restrictions": [ - {{ - "cred_def_id": "{CD_ID}" - }} - ], - "non_revoked": {{ - "from": {NOW_EPOCH}, - "to": {NOW_EPOCH} - }} - }} - }} -}}""" -) - -PRES_REQ = PresentationRequest( - comment="Test", - request_presentations_attach=[ - AttachDecorator.data_base64( - mapping=INDY_PROOF_REQ, - ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST], - ) - ], -) - - -class TestPresentationRequest(TestCase): - """Presentation request tests.""" - - def test_init(self): - """Test initializer.""" - assert PRES_REQ.request_presentations_attach[0].content == INDY_PROOF_REQ - assert PRES_REQ.indy_proof_request(0) == INDY_PROOF_REQ - - def test_type(self): - """Test type.""" - assert PRES_REQ._type == DIDCommPrefix.qualify_current(PRESENTATION_REQUEST) - - def test_deserialize(self): - """Test deserialization.""" - dump = json.dumps( - { - "@type": DIDCommPrefix.qualify_current(PRESENTATION_REQUEST), - "comment": "Hello World", - "request_presentations~attach": [ - AttachDecorator.data_base64( - mapping=INDY_PROOF_REQ, - ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST], - ).serialize() - ], - } - ) - - presentation_request = PresentationRequest.deserialize(dump) - assert type(presentation_request) is PresentationRequest - - def test_serialize(self): - """Test serialization.""" - pres_req_dict = PRES_REQ.serialize() - pres_req_dict.pop("@id") - - assert pres_req_dict == { - "@type": DIDCommPrefix.qualify_current(PRESENTATION_REQUEST), - "request_presentations~attach": [ - AttachDecorator.data_base64( - mapping=INDY_PROOF_REQ, - ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST], - ).serialize() - ], - "comment": "Test", - } - - -class TestPresentationRequestSchema(TestCase): - """Test presentation request schema""" - - def test_make_model(self): - """Test making model.""" - pres_req_dict = PRES_REQ.serialize() - """ - Looks like: { - "@type": ".../present-proof/1.0/request-presentation", - "@id": "f49773e3-bd56-4868-a5f1-456d1e6d1a16", - "comment": "Test", - "request_presentations~attach": [ - { - "mime-type": "application/json", - "data": { - "base64": "eyJuYW..." - } - } - ] - } - """ - - model_instance = PRES_REQ.deserialize(pres_req_dict) - assert isinstance(model_instance, PresentationRequest) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/__init__.py b/aries_cloudagent/protocols/present_proof/v1_0/models/__init__.py deleted file mode 100644 index 554339afe0..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Package-wide data and code.""" - -from os import environ - -UNENCRYPTED_TAGS = environ.get("EXCH_UNENCRYPTED_TAGS", "False").upper() == "TRUE" diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py b/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py deleted file mode 100644 index 7732de5997..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/presentation_exchange.py +++ /dev/null @@ -1,368 +0,0 @@ -"""Aries#0037 v1.0 presentation exchange information with non-secrets storage.""" - -import logging -from typing import Any, Mapping, Optional, Union - -from marshmallow import fields, validate - -from .....core.profile import ProfileSession -from .....indy.models.proof import IndyProof, IndyProofSchema -from .....indy.models.proof_request import IndyProofRequest, IndyProofRequestSchema -from .....messaging.models.base_record import BaseExchangeRecord, BaseExchangeSchema -from .....messaging.valid import UUID4_EXAMPLE -from .....storage.base import StorageError -from ..messages.presentation_proposal import ( - PresentationProposal, - PresentationProposalSchema, -) -from ..messages.presentation_request import ( - PresentationRequest, - PresentationRequestSchema, -) -from ..messages.presentation_webhook import V10PresentationExchangeWebhook -from . import UNENCRYPTED_TAGS - -LOGGER = logging.getLogger(__name__) - - -class V10PresentationExchange(BaseExchangeRecord): - """Represents an Aries#0037 v1.0 presentation exchange.""" - - class Meta: - """V10PresentationExchange metadata.""" - - schema_class = "V10PresentationExchangeSchema" - - RECORD_TYPE = "presentation_exchange_v10" - RECORD_ID_NAME = "presentation_exchange_id" - RECORD_TOPIC = "present_proof" - TAG_NAMES = {"~thread_id"} if UNENCRYPTED_TAGS else {"thread_id"} - - INITIATOR_SELF = "self" - INITIATOR_EXTERNAL = "external" - - ROLE_PROVER = "prover" - ROLE_VERIFIER = "verifier" - - STATE_PROPOSAL_SENT = "proposal_sent" - STATE_PROPOSAL_RECEIVED = "proposal_received" - STATE_REQUEST_SENT = "request_sent" - STATE_REQUEST_RECEIVED = "request_received" - STATE_PRESENTATION_SENT = "presentation_sent" - STATE_PRESENTATION_RECEIVED = "presentation_received" - STATE_VERIFIED = "verified" - STATE_PRESENTATION_ACKED = "presentation_acked" - STATE_ABANDONED = "abandoned" - - def __init__( - self, - *, - presentation_exchange_id: Optional[str] = None, - connection_id: Optional[str] = None, - thread_id: Optional[str] = None, - initiator: Optional[str] = None, - role: Optional[str] = None, - state: Optional[str] = None, - presentation_proposal_dict: Union[ - PresentationProposal, Mapping - ] = None, # aries message: ..._dict for historic compat on all aries msgs - presentation_request: Union[IndyProofRequest, Mapping] = None, # indy proof req - presentation_request_dict: Union[ - PresentationRequest, Mapping - ] = None, # aries message - presentation: Union[IndyProof, Mapping] = None, # indy proof - verified: Optional[str] = None, - verified_msgs: Optional[list] = None, - auto_present: bool = False, - auto_verify: bool = False, - error_msg: Optional[str] = None, - trace: bool = False, # backward compat: BaseRecord.from_storage() - auto_remove: bool = False, - **kwargs, - ): - """Initialize a new PresentationExchange.""" - super().__init__(presentation_exchange_id, state, trace=trace, **kwargs) - self.connection_id = connection_id - self.thread_id = thread_id - self.initiator = initiator - self.role = role - self.state = state - self._presentation_proposal_dict = PresentationProposal.serde( - presentation_proposal_dict - ) - self._presentation_request = IndyProofRequest.serde(presentation_request) - self._presentation_request_dict = PresentationRequest.serde( - presentation_request_dict - ) - self._presentation = IndyProof.serde(presentation) - self.verified = verified - self.verified_msgs = verified_msgs - self.auto_present = auto_present - self.auto_verify = auto_verify - self.error_msg = error_msg - self.auto_remove = auto_remove - - @property - def presentation_exchange_id(self) -> str: - """Accessor for the ID associated with this exchange.""" - return self._id - - @property - def presentation_proposal_dict(self) -> PresentationProposal: - """Accessor; get deserialized view.""" - return ( - None - if self._presentation_proposal_dict is None - else self._presentation_proposal_dict.de - ) - - @presentation_proposal_dict.setter - def presentation_proposal_dict(self, value): - """Setter; store de/serialized views.""" - self._presentation_proposal_dict = PresentationProposal.serde(value) - - @property - def presentation_request(self) -> IndyProofRequest: - """Accessor; get deserialized view.""" - return ( - None if self._presentation_request is None else self._presentation_request.de - ) - - @presentation_request.setter - def presentation_request(self, value): - """Setter; store de/serialized views.""" - self._presentation_request = IndyProofRequest.serde(value) - - @property - def presentation_request_dict(self) -> PresentationRequest: - """Accessor; get deserialized view.""" - return ( - None - if self._presentation_request_dict is None - else self._presentation_request_dict.de - ) - - @presentation_request_dict.setter - def presentation_request_dict(self, value): - """Setter; store de/serialized views.""" - self._presentation_request_dict = PresentationRequest.serde(value) - - @property - def presentation(self) -> IndyProof: - """Accessor; get deserialized view.""" - return None if self._presentation is None else self._presentation.de - - @presentation.setter - def presentation(self, value): - """Setter; store de/serialized views.""" - self._presentation = IndyProof.serde(value) - - async def save_error_state( - self, - session: ProfileSession, - *, - state: Optional[str] = None, - reason: Optional[str] = None, - log_params: Mapping[str, Any] = None, - log_override: bool = False, - ): - """Save record error state if need be; log and swallow any storage error. - - Args: - session: The profile session to use - state: The state to set - reason: A reason to add to the log - log_params: Additional parameters to log - log_override: Override configured logging regimen, print to stderr instead - """ - - if self._last_state == state: # already done - return - - self.state = state or V10PresentationExchange.STATE_ABANDONED - if reason: - self.error_msg = reason - - try: - await self.save( - session, - reason=reason, - log_params=log_params, - log_override=log_override, - ) - except StorageError as err: - LOGGER.exception(err) - - # Override - async def emit_event(self, session: ProfileSession, payload: Optional[Any] = None): - """Emit an event. - - Args: - session: The profile session to use - payload: The event payload - """ - - if not self.RECORD_TOPIC: - return - - if self.state: - topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}::{self.state}" - else: - topic = f"{self.EVENT_NAMESPACE}::{self.RECORD_TOPIC}" - - if session.profile.settings.get("debug.webhooks"): - if not payload: - payload = self.serialize() - else: - payload = V10PresentationExchangeWebhook(**self.__dict__) - payload = payload.__dict__ - - await session.profile.notify(topic, payload) - - @property - def record_value(self) -> Mapping: - """Accessor for the JSON record value generated for this credential exchange.""" - retval = { - **{ - prop: getattr(self, prop) - for prop in ( - "connection_id", - "initiator", - "role", - "state", - "auto_present", - "auto_verify", - "error_msg", - "verified", - "verified_msgs", - "trace", - "auto_remove", - ) - }, - **{ - prop: getattr(self, f"_{prop}").ser - for prop in ( - "presentation_proposal_dict", - "presentation_request", - "presentation_request_dict", - "presentation", - ) - if getattr(self, prop) is not None - }, - } - return retval - - def __eq__(self, other: Any) -> bool: - """Comparison between records.""" - return super().__eq__(other) - - -class V10PresentationExchangeSchema(BaseExchangeSchema): - """Schema for de/serialization of v1.0 presentation exchange records.""" - - class Meta: - """V10PresentationExchangeSchema metadata.""" - - model_class = V10PresentationExchange - - presentation_exchange_id = fields.Str( - required=False, - metadata={ - "description": "Presentation exchange identifier", - "example": UUID4_EXAMPLE, - }, - ) - connection_id = fields.Str( - required=False, - metadata={"description": "Connection identifier", "example": UUID4_EXAMPLE}, - ) - thread_id = fields.Str( - required=False, - metadata={"description": "Thread identifier", "example": UUID4_EXAMPLE}, - ) - initiator = fields.Str( - required=False, - validate=validate.OneOf(["self", "external"]), - metadata={ - "description": "Present-proof exchange initiator: self or external", - "example": V10PresentationExchange.INITIATOR_SELF, - }, - ) - role = fields.Str( - required=False, - validate=validate.OneOf(["prover", "verifier"]), - metadata={ - "description": "Present-proof exchange role: prover or verifier", - "example": V10PresentationExchange.ROLE_PROVER, - }, - ) - state = fields.Str( - required=False, - metadata={ - "description": "Present-proof exchange state", - "example": V10PresentationExchange.STATE_VERIFIED, - }, - ) - presentation_proposal_dict = fields.Nested( - PresentationProposalSchema(), - required=False, - metadata={"description": "Presentation proposal message"}, - ) - presentation_request = fields.Nested( - IndyProofRequestSchema(), - required=False, - metadata={ - "description": "(Indy) presentation request (also known as proof request)" - }, - ) - presentation_request_dict = fields.Nested( - PresentationRequestSchema(), - required=False, - metadata={"description": "Presentation request message"}, - ) - presentation = fields.Nested( - IndyProofSchema(), - required=False, - metadata={"description": "(Indy) presentation (also known as proof)"}, - ) - verified = fields.Str( - required=False, - validate=validate.OneOf(["true", "false"]), - metadata={ - "description": "Whether presentation is verified: true or false", - "example": "true", - }, - ) - verified_msgs = fields.List( - fields.Str( - required=False, - metadata={"description": "Proof verification warning or error information"}, - ), - required=False, - ) - auto_present = fields.Bool( - required=False, - metadata={ - "description": "Prover choice to auto-present proof as verifier requests", - "example": False, - }, - ) - auto_verify = fields.Bool( - required=False, - metadata={"description": "Verifier choice to auto-verify proof presentation"}, - ) - error_msg = fields.Str( - required=False, - metadata={"description": "Error message", "example": "Invalid structure"}, - ) - auto_remove = fields.Bool( - required=False, - dump_default=True, - metadata={ - "description": ( - "Verifier choice to remove this presentation exchange record when" - " complete" - ), - "example": False, - }, - ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/tests/__init__.py b/aries_cloudagent/protocols/present_proof/v1_0/models/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py b/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py deleted file mode 100644 index 571deee4b2..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/models/tests/test_record.py +++ /dev/null @@ -1,139 +0,0 @@ -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.tests import mock - -from ......core.in_memory import InMemoryProfile -from ......indy.models.pres_preview import ( - IndyPresAttrSpec, - IndyPresPredSpec, - IndyPresPreview, -) -from ......messaging.models.base_record import BaseExchangeRecord, BaseExchangeSchema -from ...messages.presentation_proposal import PresentationProposal -from .. import presentation_exchange as test_module -from ..presentation_exchange import V10PresentationExchange - -S_ID = "NcYxiDXkpYi6ov5FcYDi1e:2:vidya:1.0" -CD_ID = f"NcYxiDXkpYi6ov5FcYDi1e:3:CL:{S_ID}:tag1" -INDY_PROOF_REQ = { - "name": "proof-req", - "version": "1.0", - "nonce": "12345", - "requested_attributes": { - "0_player_uuid": { - "name": "player", - "restrictions": [ - { - "cred_def_id": f"{CD_ID}", - "attr::player::value": "Richie Knucklez", - } - ], - "non_revoked": { - "from": 1234567890, - "to": 1234567890, - }, - }, - "0_screencapture_uuid": { - "name": "screenCapture", - "restrictions": [{"cred_def_id": f"{CD_ID}"}], - "non_revoked": { - "from": 1234567890, - "to": 1234567890, - }, - }, - }, - "requested_predicates": { - "0_highscore_GE_uuid": { - "name": "highScore", - "p_type": ">=", - "p_value": 1000000, - "restrictions": [{"cred_def_id": f"{CD_ID}"}], - "non_revoked": { - "from": 1234567890, - "to": 1234567890, - }, - } - }, -} -PRES_PREVIEW = IndyPresPreview( - attributes=[ - IndyPresAttrSpec(name="player", cred_def_id=CD_ID, value="Richie Knucklez"), - IndyPresAttrSpec( - name="screenCapture", - cred_def_id=CD_ID, - mime_type="image/png", - value="aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - ), - ], - predicates=[ - IndyPresPredSpec( - name="highScore", cred_def_id=CD_ID, predicate=">=", threshold=1000000 - ) - ], -) - - -class BasexRecordImpl(BaseExchangeRecord): - class Meta: - schema_class = "BasexRecordImplSchema" - - RECORD_TYPE = "record" - - -class BasexRecordImplSchema(BaseExchangeSchema): - class Meta: - model_class = BasexRecordImpl - - -class TestRecord(IsolatedAsyncioTestCase): - async def test_record(self): - presentation_proposal = PresentationProposal( - comment="Hello World", presentation_proposal=PRES_PREVIEW - ) - record = V10PresentationExchange( - presentation_exchange_id="pxid", - connection_id="conn_id", - thread_id="thid", - auto_present=True, - auto_remove=True, - ) - record.presentation_proposal_dict = presentation_proposal # cover setter - record.presentation_request_dict = None # cover setter - - assert record.presentation_exchange_id == "pxid" - - assert record.record_value == { - "connection_id": "conn_id", - "initiator": None, - "presentation_proposal_dict": presentation_proposal.serialize(), - "role": None, - "state": None, - "auto_present": True, - "auto_verify": False, - "error_msg": None, - "verified": None, - "verified_msgs": None, - "trace": False, - "auto_remove": True, - } - - bx_record = BasexRecordImpl() - assert record != bx_record - - async def test_save_error_state(self): - session = InMemoryProfile.test_session() - record = V10PresentationExchange(state=None) - assert record._last_state is None - await record.save_error_state(session) # cover short circuit - - record.state = V10PresentationExchange.STATE_PROPOSAL_RECEIVED - await record.save(session) - - with mock.patch.object( - record, "save", mock.CoroutineMock() - ) as mock_save, mock.patch.object( - test_module.LOGGER, "exception", mock.MagicMock() - ) as mock_log_exc: - mock_save.side_effect = test_module.StorageError() - await record.save_error_state(session, reason="testing") - mock_log_exc.assert_called_once() diff --git a/aries_cloudagent/protocols/present_proof/v1_0/routes.py b/aries_cloudagent/protocols/present_proof/v1_0/routes.py deleted file mode 100644 index 97459a687e..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/routes.py +++ /dev/null @@ -1,1147 +0,0 @@ -"""Admin routes for presentations.""" - -import json - -from aiohttp import web -from aiohttp_apispec import ( - docs, - match_info_schema, - querystring_schema, - request_schema, - response_schema, -) -from marshmallow import fields, validate - -from ....admin.decorators.auth import tenant_authentication -from ....admin.request_context import AdminRequestContext -from ....connections.models.conn_record import ConnRecord -from ....indy.holder import IndyHolder, IndyHolderError -from ....indy.models.cred_precis import IndyCredPrecisSchema -from ....indy.models.pres_preview import IndyPresPreview, IndyPresPreviewSchema -from ....indy.models.proof import IndyPresSpecSchema -from ....indy.models.proof_request import IndyProofRequestSchema -from ....indy.util import generate_pr_nonce -from ....ledger.error import LedgerError -from ....messaging.decorators.attach_decorator import AttachDecorator -from ....messaging.models.base import BaseModelError -from ....messaging.models.openapi import OpenAPISchema -from ....messaging.models.paginated_query import PaginatedQuerySchema, get_limit_offset -from ....messaging.valid import ( - INDY_EXTRA_WQL_EXAMPLE, - INDY_EXTRA_WQL_VALIDATE, - NUM_STR_NATURAL_EXAMPLE, - NUM_STR_NATURAL_VALIDATE, - NUM_STR_WHOLE_EXAMPLE, - NUM_STR_WHOLE_VALIDATE, - UUID4_EXAMPLE, - UUID4_VALIDATE, -) -from ....storage.error import StorageError, StorageNotFoundError -from ....utils.tracing import AdminAPIMessageTracingSchema, get_timer, trace_event -from ....wallet.error import WalletNotFoundError -from . import problem_report_for_record, report_problem -from .manager import PresentationManager, PresentationManagerError -from .message_types import ATTACH_DECO_IDS, PRESENTATION_REQUEST, SPEC_URI -from .messages.presentation_problem_report import ProblemReportReason -from .messages.presentation_proposal import PresentationProposal -from .messages.presentation_request import PresentationRequest -from .models.presentation_exchange import ( - V10PresentationExchange, - V10PresentationExchangeSchema, -) - - -class V10PresentProofModuleResponseSchema(OpenAPISchema): - """Response schema for Present Proof Module.""" - - -class V10PresentationExchangeListQueryStringSchema(PaginatedQuerySchema): - """Parameters and validators for presentation exchange list query.""" - - connection_id = fields.Str( - required=False, - metadata={"description": "Connection identifier", "example": UUID4_EXAMPLE}, - ) - thread_id = fields.Str( - required=False, - metadata={"description": "Thread identifier", "example": UUID4_EXAMPLE}, - ) - role = fields.Str( - required=False, - validate=validate.OneOf( - [ - getattr(V10PresentationExchange, m) - for m in vars(V10PresentationExchange) - if m.startswith("ROLE_") - ] - ), - metadata={"description": "Role assigned in presentation exchange"}, - ) - state = fields.Str( - required=False, - validate=validate.OneOf( - [ - getattr(V10PresentationExchange, m) - for m in vars(V10PresentationExchange) - if m.startswith("STATE_") - ] - ), - metadata={"description": "Presentation exchange state"}, - ) - - -class V10PresentationExchangeListSchema(OpenAPISchema): - """Result schema for an Aries RFC 37 v1.0 presentation exchange query.""" - - results = fields.List( - fields.Nested(V10PresentationExchangeSchema()), - metadata={"description": "Aries RFC 37 v1.0 presentation exchange records"}, - ) - - -class V10PresentationSendRequestSchema(IndyPresSpecSchema): - """Request schema for sending a presentation.""" - - auto_remove = fields.Bool( - required=False, - dump_default=False, - metadata={ - "description": ( - "Whether to remove the presentation exchange record on completion" - " (overrides --preserve-exchange-records configuration setting)" - ) - }, - ) - - -class V10PresentationProposalRequestSchema(AdminAPIMessageTracingSchema): - """Request schema for sending a presentation proposal admin message.""" - - connection_id = fields.Str( - required=True, - metadata={"description": "Connection identifier", "example": UUID4_EXAMPLE}, - ) - comment = fields.Str( - required=False, - allow_none=True, - metadata={"description": "Human-readable comment"}, - ) - presentation_proposal = fields.Nested(IndyPresPreviewSchema(), required=True) - auto_present = fields.Boolean( - required=False, - dump_default=False, - metadata={ - "description": ( - "Whether to respond automatically to presentation requests, building" - " and presenting requested proof" - ) - }, - ) - auto_remove = fields.Bool( - required=False, - dump_default=False, - metadata={ - "description": ( - "Whether to remove the presentation exchange record on completion" - " (overrides --preserve-exchange-records configuration setting)" - ) - }, - ) - trace = fields.Bool( - required=False, - metadata={ - "description": "Whether to trace event (default false)", - "example": False, - }, - ) - - -class V10PresentationCreateRequestRequestSchema(AdminAPIMessageTracingSchema): - """Request schema for creating a proof request free of any connection.""" - - proof_request = fields.Nested(IndyProofRequestSchema(), required=True) - comment = fields.Str(required=False, allow_none=True) - auto_verify = fields.Bool( - required=False, - metadata={ - "description": "Verifier choice to auto-verify proof presentation", - "example": False, - }, - ) - auto_remove = fields.Bool( - required=False, - dump_default=False, - metadata={ - "description": ( - "Whether to remove the presentation exchange record on completion" - " (overrides --preserve-exchange-records configuration setting)" - ) - }, - ) - trace = fields.Bool( - required=False, - metadata={ - "description": "Whether to trace event (default false)", - "example": False, - }, - ) - - -class V10PresentationSendRequestRequestSchema(V10PresentationCreateRequestRequestSchema): - """Request schema for sending a proof request on a connection.""" - - connection_id = fields.Str( - required=True, - metadata={"description": "Connection identifier", "example": UUID4_EXAMPLE}, - ) - - -class V10PresentationSendRequestToProposalSchema(AdminAPIMessageTracingSchema): - """Request schema for sending a proof request bound to a proposal.""" - - auto_verify = fields.Bool( - required=False, - metadata={ - "description": "Verifier choice to auto-verify proof presentation", - "example": False, - }, - ) - auto_remove = fields.Bool( - required=False, - dump_default=False, - metadata={ - "description": ( - "Whether to remove the presentation exchange record on completion" - " (overrides --preserve-exchange-records configuration setting)" - ) - }, - ) - trace = fields.Bool( - required=False, - metadata={ - "description": "Whether to trace event (default false)", - "example": False, - }, - ) - - -class CredentialsFetchQueryStringSchema(OpenAPISchema): - """Parameters and validators for credentials fetch request query string.""" - - referent = fields.Str( - required=False, - metadata={ - "description": "Proof request referents of interest, comma-separated", - "example": "1_name_uuid,2_score_uuid", - }, - ) - start = fields.Str( - required=False, - validate=NUM_STR_WHOLE_VALIDATE, - metadata={ - "description": "Start index", - "strict": True, - "example": NUM_STR_WHOLE_EXAMPLE, - }, - ) - count = fields.Str( - required=False, - validate=NUM_STR_NATURAL_VALIDATE, - metadata={ - "description": "Maximum number to retrieve", - "example": NUM_STR_NATURAL_EXAMPLE, - }, - ) - extra_query = fields.Str( - required=False, - validate=INDY_EXTRA_WQL_VALIDATE, - metadata={ - "description": "(JSON) object mapping referents to extra WQL queries", - "example": INDY_EXTRA_WQL_EXAMPLE, - }, - ) - - -class V10PresentationProblemReportRequestSchema(OpenAPISchema): - """Request schema for sending problem report.""" - - description = fields.Str(required=True) - - -class V10PresExIdMatchInfoSchema(OpenAPISchema): - """Path parameters and validators for request taking presentation exchange id.""" - - pres_ex_id = fields.Str( - required=True, - validate=UUID4_VALIDATE, - metadata={ - "description": "Presentation exchange identifier", - "example": UUID4_EXAMPLE, - }, - ) - - -@docs( - tags=["present-proof v1.0"], - summary="Fetch all present-proof exchange records", - deprecated=True, -) -@querystring_schema(V10PresentationExchangeListQueryStringSchema) -@response_schema(V10PresentationExchangeListSchema(), 200, description="") -@tenant_authentication -async def presentation_exchange_list(request: web.BaseRequest): - """Request handler for searching presentation exchange records. - - Args: - request: aiohttp request object - - Returns: - The presentation exchange list response - - """ - context: AdminRequestContext = request["context"] - tag_filter = {} - if "thread_id" in request.query and request.query["thread_id"] != "": - tag_filter["thread_id"] = request.query["thread_id"] - post_filter = { - k: request.query[k] - for k in ("connection_id", "role", "state") - if request.query.get(k, "") != "" - } - - limit, offset = get_limit_offset(request) - - try: - async with context.profile.session() as session: - records = await V10PresentationExchange.query( - session=session, - tag_filter=tag_filter, - limit=limit, - offset=offset, - post_filter_positive=post_filter, - ) - results = [record.serialize() for record in records] - except (StorageError, BaseModelError) as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - return web.json_response({"results": results}) - - -@docs( - tags=["present-proof v1.0"], - summary="Fetch a single presentation exchange record", - deprecated=True, -) -@match_info_schema(V10PresExIdMatchInfoSchema()) -@response_schema(V10PresentationExchangeSchema(), 200, description="") -@tenant_authentication -async def presentation_exchange_retrieve(request: web.BaseRequest): - """Request handler for fetching a single presentation exchange record. - - Args: - request: aiohttp request object - - Returns: - The presentation exchange record response - - """ - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - presentation_exchange_id = request.match_info["pres_ex_id"] - pres_ex_record = None - try: - async with profile.session() as session: - pres_ex_record = await V10PresentationExchange.retrieve_by_id( - session, presentation_exchange_id - ) - result = pres_ex_record.serialize() - except StorageNotFoundError as err: - # no such pres ex record: not protocol error, user fat-fingered id - raise web.HTTPNotFound(reason=err.roll_up) from err - except (BaseModelError, StorageError) as err: - # present but broken or hopeless: protocol error - if pres_ex_record: - async with profile.session() as session: - await pres_ex_record.save_error_state(session, reason=err.roll_up) - await report_problem( - err, - ProblemReportReason.ABANDONED.value, - web.HTTPBadRequest, - pres_ex_record, - outbound_handler, - ) - - return web.json_response(result) - - -@docs( - tags=["present-proof v1.0"], - summary="Fetch credentials for a presentation request from wallet", - deprecated=True, -) -@match_info_schema(V10PresExIdMatchInfoSchema()) -@querystring_schema(CredentialsFetchQueryStringSchema()) -@response_schema(IndyCredPrecisSchema(many=True), 200, description="") -@tenant_authentication -async def presentation_exchange_credentials_list(request: web.BaseRequest): - """Request handler for searching applicable credential records. - - Args: - request: aiohttp request object - - Returns: - The credential list response - - """ - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - presentation_exchange_id = request.match_info["pres_ex_id"] - referents = request.query.get("referent") - presentation_referents = ( - (r.strip() for r in referents.split(",")) if referents else () - ) - - try: - async with profile.session() as session: - pres_ex_record = await V10PresentationExchange.retrieve_by_id( - session, presentation_exchange_id - ) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - - start = request.query.get("start") - count = request.query.get("count") - - # url encoded json extra_query - encoded_extra_query = request.query.get("extra_query") or "{}" - extra_query = json.loads(encoded_extra_query) - - # defaults - start = int(start) if isinstance(start, str) else 0 - count = int(count) if isinstance(count, str) else 10 - - holder = profile.inject(IndyHolder) - try: - credentials = await holder.get_credentials_for_presentation_request_by_referent( - pres_ex_record._presentation_request.ser, - presentation_referents, - start, - count, - extra_query, - ) - except IndyHolderError as err: - if pres_ex_record: - async with profile.session() as session: - await pres_ex_record.save_error_state(session, reason=err.roll_up) - await report_problem( - err, - ProblemReportReason.ABANDONED.value, - web.HTTPBadRequest, - pres_ex_record, - outbound_handler, - ) - - pres_ex_record.log_state( - "Retrieved presentation credentials", - { - "presentation_exchange_id": presentation_exchange_id, - "referents": presentation_referents, - "extra_query": extra_query, - "credentials": credentials, - }, - settings=context.settings, - ) - return web.json_response(credentials) - - -@docs( - tags=["present-proof v1.0"], - summary="Sends a presentation proposal", - deprecated=True, -) -@request_schema(V10PresentationProposalRequestSchema()) -@response_schema(V10PresentationExchangeSchema(), 200, description="") -@tenant_authentication -async def presentation_exchange_send_proposal(request: web.BaseRequest): - """Request handler for sending a presentation proposal. - - Args: - request: aiohttp request object - - Returns: - The presentation exchange details - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - body = await request.json() - - comment = body.get("comment") - connection_id = body.get("connection_id") - - # Aries RFC 37 calls it a proposal in the proposal struct but it's of type preview - presentation_preview = body.get("presentation_proposal") - connection_record = None - async with profile.session() as session: - try: - connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - presentation_proposal_message = PresentationProposal( - comment=comment, - presentation_proposal=IndyPresPreview.deserialize(presentation_preview), - ) - except (BaseModelError, StorageError) as err: - # other party does not care about our false protocol start - raise web.HTTPBadRequest(reason=err.roll_up) - - if not connection_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") - - trace_msg = body.get("trace") - presentation_proposal_message.assign_trace_decorator( - context.settings, - trace_msg, - ) - auto_present = body.get( - "auto_present", context.settings.get("debug.auto_respond_presentation_request") - ) - auto_remove = body.get("auto_remove") - - presentation_manager = PresentationManager(profile) - pres_ex_record = None - try: - pres_ex_record = await presentation_manager.create_exchange_for_proposal( - connection_id=connection_id, - presentation_proposal_message=presentation_proposal_message, - auto_present=auto_present, - auto_remove=auto_remove, - ) - result = pres_ex_record.serialize() - except (BaseModelError, StorageError) as err: - if pres_ex_record: - async with profile.session() as session: - await pres_ex_record.save_error_state(session, reason=err.roll_up) - # other party does not care about our false protocol start - raise web.HTTPBadRequest(reason=err.roll_up) - - await outbound_handler(presentation_proposal_message, connection_id=connection_id) - - trace_event( - context.settings, - presentation_proposal_message, - outcome="presentation_exchange_propose.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["present-proof v1.0"], - summary="Creates a presentation request not bound to any proposal or connection", - deprecated=True, -) -@request_schema(V10PresentationCreateRequestRequestSchema()) -@response_schema(V10PresentationExchangeSchema(), 200, description="") -@tenant_authentication -async def presentation_exchange_create_request(request: web.BaseRequest): - """Request handler for creating a free presentation request. - - The presentation request will not be bound to any proposal - or existing connection. - - Args: - request: aiohttp request object - - Returns: - The presentation exchange details - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - - body = await request.json() - - comment = body.get("comment") - indy_proof_request = body.get("proof_request") - if not indy_proof_request.get("nonce"): - indy_proof_request["nonce"] = await generate_pr_nonce() - - presentation_request_message = PresentationRequest( - comment=comment, - request_presentations_attach=[ - AttachDecorator.data_base64( - mapping=indy_proof_request, - ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST], - ) - ], - ) - auto_verify = body.get( - "auto_verify", context.settings.get("debug.auto_verify_presentation") - ) - auto_remove = body.get("auto_remove") - trace_msg = body.get("trace") - presentation_request_message.assign_trace_decorator( - context.settings, - trace_msg, - ) - - pres_ex_record = None - try: - presentation_manager = PresentationManager(profile) - pres_ex_record = await presentation_manager.create_exchange_for_request( - connection_id=None, - presentation_request_message=presentation_request_message, - auto_verify=auto_verify, - auto_remove=auto_remove, - ) - result = pres_ex_record.serialize() - except (BaseModelError, StorageError) as err: - if pres_ex_record: - async with profile.session() as session: - await pres_ex_record.save_error_state(session, reason=err.roll_up) - # other party does not care about our false protocol start - raise web.HTTPBadRequest(reason=err.roll_up) - - trace_event( - context.settings, - presentation_request_message, - outcome="presentation_exchange_create_request.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["present-proof v1.0"], - summary="Sends a free presentation request not bound to any proposal", - deprecated=True, -) -@request_schema(V10PresentationSendRequestRequestSchema()) -@response_schema(V10PresentationExchangeSchema(), 200, description="") -@tenant_authentication -async def presentation_exchange_send_free_request(request: web.BaseRequest): - """Request handler for sending a presentation request free from any proposal. - - Args: - request: aiohttp request object - - Returns: - The presentation exchange details - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - body = await request.json() - - connection_id = body.get("connection_id") - async with profile.session() as session: - try: - connection_record = await ConnRecord.retrieve_by_id(session, connection_id) - except StorageNotFoundError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - if not connection_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") - - comment = body.get("comment") - indy_proof_request = body.get("proof_request") - if not indy_proof_request.get("nonce"): - indy_proof_request["nonce"] = await generate_pr_nonce() - - presentation_request_message = PresentationRequest( - comment=comment, - request_presentations_attach=[ - AttachDecorator.data_base64( - mapping=indy_proof_request, - ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST], - ) - ], - ) - trace_msg = body.get("trace") - presentation_request_message.assign_trace_decorator( - context.settings, - trace_msg, - ) - auto_verify = body.get( - "auto_verify", context.settings.get("debug.auto_verify_presentation") - ) - auto_remove = body.get("auto_remove") - - pres_ex_record = None - try: - presentation_manager = PresentationManager(profile) - pres_ex_record = await presentation_manager.create_exchange_for_request( - connection_id=connection_id, - presentation_request_message=presentation_request_message, - auto_verify=auto_verify, - auto_remove=auto_remove, - ) - result = pres_ex_record.serialize() - except (BaseModelError, StorageError) as err: - if pres_ex_record: - async with profile.session() as session: - await pres_ex_record.save_error_state(session, reason=err.roll_up) - # other party does not care about our false protocol start - raise web.HTTPBadRequest(reason=err.roll_up) - - await outbound_handler(presentation_request_message, connection_id=connection_id) - - trace_event( - context.settings, - presentation_request_message, - outcome="presentation_exchange_send_request.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["present-proof v1.0"], - summary="Sends a presentation request in reference to a proposal", - deprecated=True, -) -@match_info_schema(V10PresExIdMatchInfoSchema()) -@request_schema(V10PresentationSendRequestToProposalSchema()) -@response_schema(V10PresentationExchangeSchema(), 200, description="") -@tenant_authentication -async def presentation_exchange_send_bound_request(request: web.BaseRequest): - """Request handler for sending a presentation request bound to a proposal. - - Args: - request: aiohttp request object - - Returns: - The presentation exchange details - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - body = await request.json() - - presentation_exchange_id = request.match_info["pres_ex_id"] - pres_ex_record = None - async with profile.session() as session: - try: - pres_ex_record = await V10PresentationExchange.retrieve_by_id( - session, presentation_exchange_id - ) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - - if pres_ex_record.state != (V10PresentationExchange.STATE_PROPOSAL_RECEIVED): - raise web.HTTPBadRequest( - reason=( - f"Presentation exchange {presentation_exchange_id} " - f"in {pres_ex_record.state} state " - f"(must be {V10PresentationExchange.STATE_PROPOSAL_RECEIVED})" - ) - ) - conn_id = pres_ex_record.connection_id - - try: - connection_record = await ConnRecord.retrieve_by_id(session, conn_id) - except StorageError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - if not connection_record.is_ready: - raise web.HTTPForbidden(reason=f"Connection {conn_id} not ready") - - pres_ex_record.auto_verify = body.get( - "auto_verify", context.settings.get("debug.auto_verify_presentation") - ) - pres_ex_record.auto_remove = body.get("auto_remove") - - try: - presentation_manager = PresentationManager(profile) - ( - pres_ex_record, - presentation_request_message, - ) = await presentation_manager.create_bound_request(pres_ex_record) - result = pres_ex_record.serialize() - except (BaseModelError, LedgerError, StorageError) as err: - if pres_ex_record: - async with profile.session() as session: - await pres_ex_record.save_error_state(session, reason=err.roll_up) - # other party cares that we cannot continue protocol - await report_problem( - err, - ProblemReportReason.ABANDONED.value, - web.HTTPBadRequest, - pres_ex_record, - outbound_handler, - ) - - trace_msg = body.get("trace") - presentation_request_message.assign_trace_decorator( - context.settings, - trace_msg, - ) - await outbound_handler(presentation_request_message, connection_id=conn_id) - - trace_event( - context.settings, - presentation_request_message, - outcome="presentation_exchange_send_request.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["present-proof v1.0"], - summary="Sends a proof presentation", - deprecated=True, -) -@match_info_schema(V10PresExIdMatchInfoSchema()) -@request_schema(V10PresentationSendRequestSchema()) -@response_schema(V10PresentationExchangeSchema(), description="") -@tenant_authentication -async def presentation_exchange_send_presentation(request: web.BaseRequest): - """Request handler for sending a presentation. - - Args: - request: aiohttp request object - - Returns: - The presentation exchange details - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - presentation_exchange_id = request.match_info["pres_ex_id"] - body = await request.json() - - pres_ex_record = None - async with profile.session() as session: - try: - pres_ex_record = await V10PresentationExchange.retrieve_by_id( - session, presentation_exchange_id - ) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - - if pres_ex_record.state != (V10PresentationExchange.STATE_REQUEST_RECEIVED): - raise web.HTTPBadRequest( - reason=( - f"Presentation exchange {presentation_exchange_id} " - f"in {pres_ex_record.state} state " - f"(must be {V10PresentationExchange.STATE_REQUEST_RECEIVED})" - ) - ) - - auto_remove = body.get("auto_remove") - if auto_remove is None: - auto_remove = not profile.settings.get("preserve_exchange_records") - - pres_ex_record.auto_remove = auto_remove - - # Fetch connection if exchange has record - connection_record = None - if pres_ex_record.connection_id: - try: - connection_record = await ConnRecord.retrieve_by_id( - session, pres_ex_record.connection_id - ) - except StorageNotFoundError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - if connection_record and not connection_record.is_ready: - raise web.HTTPForbidden( - reason=f"Connection {connection_record.connection_id} not ready" - ) - - try: - presentation_manager = PresentationManager(profile) - ( - pres_ex_record, - presentation_message, - ) = await presentation_manager.create_presentation( - pres_ex_record, - { - "self_attested_attributes": body.get("self_attested_attributes"), - "requested_attributes": body.get("requested_attributes"), - "requested_predicates": body.get("requested_predicates"), - }, - comment=body.get("comment"), - ) - result = pres_ex_record.serialize() - except ( - BaseModelError, - IndyHolderError, - LedgerError, - StorageError, - WalletNotFoundError, - ) as err: - if pres_ex_record: - async with profile.session() as session: - await pres_ex_record.save_error_state(session, reason=err.roll_up) - # other party cares that we cannot continue protocol - await report_problem( - err, - ProblemReportReason.ABANDONED.value, - web.HTTPBadRequest, - pres_ex_record, - outbound_handler, - ) - - trace_msg = body.get("trace") - presentation_message.assign_trace_decorator( - context.settings, - trace_msg, - ) - await outbound_handler( - presentation_message, connection_id=pres_ex_record.connection_id - ) - - trace_event( - context.settings, - presentation_message, - outcome="presentation_exchange_send_request.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["present-proof v1.0"], - summary="Verify a received presentation", - deprecated=True, -) -@match_info_schema(V10PresExIdMatchInfoSchema()) -@response_schema(V10PresentationExchangeSchema(), description="") -@tenant_authentication -async def presentation_exchange_verify_presentation(request: web.BaseRequest): - """Request handler for verifying a presentation request. - - Args: - request: aiohttp request object - - Returns: - The presentation exchange details - - """ - r_time = get_timer() - - context: AdminRequestContext = request["context"] - profile = context.profile - outbound_handler = request["outbound_message_router"] - - presentation_exchange_id = request.match_info["pres_ex_id"] - - pres_ex_record = None - async with profile.session() as session: - try: - pres_ex_record = await V10PresentationExchange.retrieve_by_id( - session, presentation_exchange_id - ) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - - if pres_ex_record.state != (V10PresentationExchange.STATE_PRESENTATION_RECEIVED): - raise web.HTTPBadRequest( - reason=( - f"Presentation exchange {presentation_exchange_id} " - f"in {pres_ex_record.state} state " - f"(must be {V10PresentationExchange.STATE_PRESENTATION_RECEIVED})" - ) - ) - - try: - presentation_manager = PresentationManager(profile) - pres_ex_record = await presentation_manager.verify_presentation(pres_ex_record) - result = pres_ex_record.serialize() - except (BaseModelError, LedgerError, StorageError) as err: - if pres_ex_record: - async with profile.session() as session: - await pres_ex_record.save_error_state(session, reason=err.roll_up) - # other party cares that we cannot continue protocol - await report_problem( - err, - ProblemReportReason.ABANDONED.value, - web.HTTPBadRequest, - pres_ex_record, - outbound_handler, - ) - except PresentationManagerError as err: - return web.HTTPBadRequest(reason=err.roll_up) - - trace_event( - context.settings, - pres_ex_record, - outcome="presentation_exchange_verify.END", - perf_counter=r_time, - ) - - return web.json_response(result) - - -@docs( - tags=["present-proof v1.0"], - summary="Send a problem report for presentation exchange", - deprecated=True, -) -@match_info_schema(V10PresExIdMatchInfoSchema()) -@request_schema(V10PresentationProblemReportRequestSchema()) -@response_schema(V10PresentProofModuleResponseSchema(), 200, description="") -@tenant_authentication -async def presentation_exchange_problem_report(request: web.BaseRequest): - """Request handler for sending problem report. - - Args: - request: aiohttp request object - - """ - context: AdminRequestContext = request["context"] - outbound_handler = request["outbound_message_router"] - - pres_ex_id = request.match_info["pres_ex_id"] - body = await request.json() - description = body["description"] - - try: - async with await context.profile.session() as session: - pres_ex_record = await V10PresentationExchange.retrieve_by_id( - session, pres_ex_id - ) - report = problem_report_for_record(pres_ex_record, description) - await pres_ex_record.save_error_state( - session, - reason=f"created problem report: {description}", - ) - except StorageNotFoundError as err: # other party does not care about meta-problems - raise web.HTTPNotFound(reason=err.roll_up) from err - except StorageError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - await outbound_handler(report, connection_id=pres_ex_record.connection_id) - - return web.json_response({}) - - -@docs( - tags=["present-proof v1.0"], - summary="Remove an existing presentation exchange record", - deprecated=True, -) -@match_info_schema(V10PresExIdMatchInfoSchema()) -@response_schema(V10PresentProofModuleResponseSchema(), description="") -@tenant_authentication -async def presentation_exchange_remove(request: web.BaseRequest): - """Request handler for removing a presentation exchange record. - - Args: - request: aiohttp request object - - """ - context: AdminRequestContext = request["context"] - - presentation_exchange_id = request.match_info["pres_ex_id"] - pres_ex_record = None - try: - async with context.profile.session() as session: - pres_ex_record = await V10PresentationExchange.retrieve_by_id( - session, presentation_exchange_id - ) - await pres_ex_record.delete_record(session) - except StorageNotFoundError as err: - raise web.HTTPNotFound(reason=err.roll_up) from err - except StorageError as err: - raise web.HTTPBadRequest(reason=err.roll_up) from err - - return web.json_response({}) - - -async def register(app: web.Application): - """Register routes.""" - - app.add_routes( - [ - web.get( - "/present-proof/records", - presentation_exchange_list, - allow_head=False, - ), - web.get( - "/present-proof/records/{pres_ex_id}", - presentation_exchange_retrieve, - allow_head=False, - ), - web.get( - "/present-proof/records/{pres_ex_id}/credentials", - presentation_exchange_credentials_list, - allow_head=False, - ), - web.post( - "/present-proof/send-proposal", - presentation_exchange_send_proposal, - ), - web.post( - "/present-proof/create-request", - presentation_exchange_create_request, - ), - web.post( - "/present-proof/send-request", - presentation_exchange_send_free_request, - ), - web.post( - "/present-proof/records/{pres_ex_id}/send-request", - presentation_exchange_send_bound_request, - ), - web.post( - "/present-proof/records/{pres_ex_id}/send-presentation", - presentation_exchange_send_presentation, - ), - web.post( - "/present-proof/records/{pres_ex_id}/verify-presentation", - presentation_exchange_verify_presentation, - ), - web.post( - "/present-proof/records/{pres_ex_id}/problem-report", - presentation_exchange_problem_report, - ), - web.delete( - "/present-proof/records/{pres_ex_id}", - presentation_exchange_remove, - ), - ] - ) - - -def post_process_routes(app: web.Application): - """Amend swagger API.""" - - # Add top-level tags description - if "tags" not in app._state["swagger_dict"]: - app._state["swagger_dict"]["tags"] = [] - app._state["swagger_dict"]["tags"].append( - { - "name": "present-proof v1.0", - "description": "Proof presentation v1.0", - "externalDocs": {"description": "Specification", "url": SPEC_URI}, - } - ) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/tests/__init__.py b/aries_cloudagent/protocols/present_proof/v1_0/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 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 deleted file mode 100644 index 1a48dba753..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_manager.py +++ /dev/null @@ -1,1396 +0,0 @@ -import json -from time import time -from unittest import IsolatedAsyncioTestCase - -from aries_cloudagent.protocols.issue_credential.v1_0.models.credential_exchange import ( - V10CredentialExchange, -) -from aries_cloudagent.tests import mock - -from .....core.in_memory import InMemoryProfile -from .....indy.holder import IndyHolder, IndyHolderError -from .....indy.models.pres_preview import ( - IndyPresAttrSpec, - IndyPresPredSpec, - IndyPresPreview, -) -from .....indy.models.xform import indy_proof_req_preview2indy_requested_creds -from .....indy.verifier import IndyVerifier -from .....ledger.base import BaseLedger -from .....ledger.multiple_ledger.ledger_requests_executor import ( - IndyLedgerRequestsExecutor, -) -from .....messaging.decorators.attach_decorator import AttachDecorator -from .....messaging.responder import BaseResponder, MockResponder -from ....didcomm_prefix import DIDCommPrefix -from ...indy import pres_exch_handler as test_indy_util_module -from .. import manager as test_module -from ..manager import PresentationManager, PresentationManagerError -from ..message_types import ATTACH_DECO_IDS, PRESENTATION, PRESENTATION_REQUEST -from ..messages.presentation import Presentation -from ..messages.presentation_problem_report import PresentationProblemReport -from ..messages.presentation_proposal import PresentationProposal -from ..messages.presentation_request import PresentationRequest -from ..models.presentation_exchange import V10PresentationExchange - -NOW = int(time()) -CONN_ID = "connection_id" -ISSUER_DID = "NcYxiDXkpYi6ov5FcYDi1e" -S_ID = f"{ISSUER_DID}:2:vidya:1.0" -CD_ID = f"{ISSUER_DID}:3:CL:{S_ID}:tag1" -RR_ID = f"{ISSUER_DID}:4:{CD_ID}:CL_ACCUM:0" -PRES_PREVIEW = IndyPresPreview( - attributes=[ - IndyPresAttrSpec(name="player", cred_def_id=CD_ID, value="Richie Knucklez"), - IndyPresAttrSpec( - name="screenCapture", - cred_def_id=CD_ID, - mime_type="image/png", - value="aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - ), - ], - predicates=[ - IndyPresPredSpec( - name="highScore", cred_def_id=CD_ID, predicate=">=", threshold=1000000 - ) - ], -) -PRES_PREVIEW_NAMES = IndyPresPreview( - attributes=[ - IndyPresAttrSpec( - name="player", cred_def_id=CD_ID, value="Richie Knucklez", referent="0" - ), - IndyPresAttrSpec( - name="screenCapture", - cred_def_id=CD_ID, - mime_type="image/png", - value="aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - referent="0", - ), - ], - predicates=[ - IndyPresPredSpec( - name="highScore", cred_def_id=CD_ID, predicate=">=", threshold=1000000 - ) - ], -) -PROOF_REQ_NAME = "name" -PROOF_REQ_VERSION = "1.0" -PROOF_REQ_NONCE = "12345" -INDY_PROOF = { - "proof": { - "proofs": [ - { - "primary_proof": { - "eq_proof": { - "revealed_attrs": { - "player": "51643998292319337989", - "screencapture": "124831723185628395682368329568235681", - }, - "a_prime": "98381845469564775640588", - "e": "2889201651469315129053056279820725958192110265136", - "v": "337782521199137176224", - "m": { - "master_secret": "88675074759262558623", - "date": "3707627155679953691027082306", - "highscore": "251972383037120760793174059437326", - }, - "m2": "2892781443118611948331343540849982215419978654911341", - }, - "ge_proofs": [ - { - "u": { - "0": "99189584890680947709857922351898933228959", - "3": "974568160016086782335901983921278203", - "2": "127290395299", - "1": "7521808223922", - }, - "r": { - "3": "247458", - "2": "263046", - "1": "285214", - "DELTA": "4007402", - "0": "12066738", - }, - "mj": "1507606", - "alpha": "20251550018805200", - "t": { - "1": "1262519732727", - "3": "82102416", - "0": "100578099981822", - "2": "47291", - "DELTA": "556736142765", - }, - "predicate": { - "attr_name": "highscore", - "p_type": "GE", - "value": 1000000, - }, - } - ], - }, - "non_revoc_proof": { - "x_list": { - "rho": "128121489ACD4D778ECE", - "r": "1890DEFBB8A254", - "r_prime": "0A0861FFE96C", - "r_prime_prime": "058376CE", - "r_prime_prime_prime": "188DF30745A595", - "o": "0D0F7FA1", - "o_prime": "28165", - "m": "0187A9817897FC", - "m_prime": "91261D96B", - "t": "10FE96", - "t_prime": "10856A", - "m2": "B136089AAF", - "s": "018969A6D", - "c": "09186B6A", - }, - "c_list": { - "e": "6 1B161", - "d": "6 19E861869", - "a": "6 541441EE2", - "g": "6 7601B068C", - "w": "21 10DE6 4 AAAA 5 2458 6 16161", - "s": "21 09616 4 1986 5 9797 6 BBBBB", - "u": "21 3213123 4 0616FFE 5 323 6 110861861", - }, - }, - } - ], - "aggregated_proof": { - "c_hash": "81147637626525127013830996", - "c_list": [ - [3, 18, 46, 12], - [3, 136, 2, 39], - [100, 111, 148, 193], - [1, 123, 11, 152], - [2, 138, 162, 227], - [1, 239, 33, 47], - ], - }, - }, - "requested_proof": { - "revealed_attrs": { - "0_player_uuid": { - "sub_proof_index": 0, - "raw": "Richie Knucklez", - "encoded": "516439982", - }, - "0_screencapture_uuid": { - "sub_proof_index": 0, - "raw": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - "encoded": "4434954949", - }, - }, - "self_attested_attrs": {}, - "unrevealed_attrs": {}, - "predicates": {"0_highscore_GE_uuid": {"sub_proof_index": 0}}, - }, - "identifiers": [ - { - "schema_id": S_ID, - "cred_def_id": CD_ID, - "rev_reg_id": RR_ID, - "timestamp": NOW, - } - ], -} -PRES = Presentation( - comment="Test", - presentations_attach=[ - AttachDecorator.data_base64( - mapping=INDY_PROOF, - ident=ATTACH_DECO_IDS[PRESENTATION], - ) - ], -) -PRES.assign_thread_id("dummy") - - -class TestPresentationManager(IsolatedAsyncioTestCase): - async def asyncSetUp(self): - self.profile = InMemoryProfile.test_profile() - injector = self.profile.context.injector - - Ledger = mock.MagicMock(BaseLedger, autospec=True) - self.ledger = Ledger() - self.ledger.get_schema = mock.CoroutineMock(return_value=mock.MagicMock()) - self.ledger.get_credential_definition = mock.CoroutineMock( - return_value={"value": {"revocation": {"...": "..."}}} - ) - self.ledger.get_revoc_reg_def = mock.CoroutineMock( - return_value={ - "ver": "1.0", - "id": RR_ID, - "revocDefType": "CL_ACCUM", - "tag": RR_ID.split(":")[-1], - "credDefId": CD_ID, - "value": { - "IssuanceType": "ISSUANCE_BY_DEFAULT", - "maxCredNum": 1000, - "publicKeys": {"accumKey": {"z": "1 ..."}}, - "tailsHash": "3MLjUFQz9x9n5u9rFu8Ba9C5bo4HNFjkPNc54jZPSNaZ", - "tailsLocation": "http://sample.ca/path", - }, - } - ) - self.ledger.get_revoc_reg_delta = mock.CoroutineMock( - return_value=( - { - "ver": "1.0", - "value": {"prevAccum": "1 ...", "accum": "21 ...", "issued": [1]}, - }, - NOW, - ) - ) - self.ledger.get_revoc_reg_entry = mock.CoroutineMock( - return_value=( - { - "ver": "1.0", - "value": {"prevAccum": "1 ...", "accum": "21 ...", "issued": [1]}, - }, - NOW, - ) - ) - injector.bind_instance(BaseLedger, self.ledger) - injector.bind_instance( - IndyLedgerRequestsExecutor, - mock.MagicMock( - get_ledger_for_identifier=mock.CoroutineMock( - return_value=(None, self.ledger) - ) - ), - ) - Holder = mock.MagicMock(IndyHolder, autospec=True) - self.holder = Holder() - get_creds = mock.CoroutineMock( - return_value=( - { - "cred_info": { - "referent": "dummy_reft", - "attrs": { - "player": "Richie Knucklez", - "screenCapture": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - "highScore": "1234560", - }, - } - }, # leave this comma: return a tuple - ) - ) - self.holder.get_credentials_for_presentation_request_by_referent = get_creds - self.holder.get_credential = 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 = mock.CoroutineMock(return_value="{}") - self.holder.create_revocation_state = mock.CoroutineMock( - return_value=json.dumps( - { - "witness": {"omega": "1 ..."}, - "rev_reg": {"accum": "21 ..."}, - "timestamp": NOW, - } - ) - ) - injector.bind_instance(IndyHolder, self.holder) - - Verifier = mock.MagicMock(IndyVerifier, autospec=True) - self.verifier = Verifier() - self.verifier.verify_presentation = mock.CoroutineMock(return_value=("true", [])) - injector.bind_instance(IndyVerifier, self.verifier) - - self.manager = PresentationManager(self.profile) - - async def test_record_eq(self): - same = [ - V10PresentationExchange( - presentation_exchange_id="dummy-0", - thread_id="thread-0", - role=V10PresentationExchange.ROLE_PROVER, - ) - ] * 2 - diff = [ - V10PresentationExchange( - presentation_exchange_id="dummy-1", - role=V10PresentationExchange.ROLE_PROVER, - ), - V10PresentationExchange( - presentation_exchange_id="dummy-0", - thread_id="thread-1", - role=V10PresentationExchange.ROLE_PROVER, - ), - V10PresentationExchange( - presentation_exchange_id="dummy-1", - thread_id="thread-0", - role=V10PresentationExchange.ROLE_VERIFIER, - ), - ] - - for i in range(len(same) - 1): - for j in range(i, len(same)): - assert same[i] == same[j] - - for i in range(len(diff) - 1): - for j in range(i, len(diff)): - assert diff[i] == diff[j] if i == j else diff[i] != diff[j] - - async def test_create_exchange_for_proposal(self): - proposal = PresentationProposal() - - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object(PresentationProposal, "serialize", autospec=True): - exchange = await self.manager.create_exchange_for_proposal( - CONN_ID, - proposal, - auto_present=None, - auto_remove=True, - ) - save_ex.assert_called_once() - - assert exchange.thread_id == proposal._thread_id - assert exchange.initiator == V10PresentationExchange.INITIATOR_SELF - assert exchange.role == V10PresentationExchange.ROLE_PROVER - assert exchange.state == V10PresentationExchange.STATE_PROPOSAL_SENT - assert exchange.auto_remove is True - - async def test_receive_proposal(self): - connection_record = mock.MagicMock(connection_id=CONN_ID) - proposal = PresentationProposal() - - with mock.patch.object(V10PresentationExchange, "save", autospec=True) as save_ex: - exchange = await self.manager.receive_proposal(proposal, connection_record) - save_ex.assert_called_once() - - assert exchange.state == V10PresentationExchange.STATE_PROPOSAL_RECEIVED - - async def test_create_bound_request(self): - comment = "comment" - - proposal = PresentationProposal(presentation_proposal=PRES_PREVIEW) - exchange = V10PresentationExchange( - presentation_proposal_dict=proposal.serialize(), - role=V10PresentationExchange.ROLE_VERIFIER, - ) - exchange.save = mock.CoroutineMock() - (ret_exchange, pres_req_msg) = await self.manager.create_bound_request( - presentation_exchange_record=exchange, - name=PROOF_REQ_NAME, - version=PROOF_REQ_VERSION, - nonce=PROOF_REQ_NONCE, - comment=comment, - ) - assert ret_exchange is exchange - exchange.save.assert_called_once() - - async def test_create_exchange_for_request(self): - indy_proof_req = await PRES_PREVIEW.indy_proof_request( - name=PROOF_REQ_NAME, - version=PROOF_REQ_VERSION, - nonce=PROOF_REQ_NONCE, - profile=self.profile, - ) - pres_req = PresentationRequest( - request_presentations_attach=[ - AttachDecorator.data_base64( - mapping=indy_proof_req, - ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST], - ) - ] - ) - - with mock.patch.object(V10PresentationExchange, "save", autospec=True) as save_ex: - exchange = await self.manager.create_exchange_for_request( - CONN_ID, - pres_req, - auto_remove=True, - ) - save_ex.assert_called_once() - - assert exchange.thread_id == pres_req._thread_id - assert exchange.initiator == V10PresentationExchange.INITIATOR_SELF - assert exchange.role == V10PresentationExchange.ROLE_VERIFIER - assert exchange.state == V10PresentationExchange.STATE_REQUEST_SENT - assert exchange.auto_remove is True - - async def test_receive_request(self): - exchange_in = V10PresentationExchange() - - with mock.patch.object(V10PresentationExchange, "save", autospec=True) as save_ex: - exchange_out = await self.manager.receive_request(exchange_in) - save_ex.assert_called_once() - - assert exchange_out.state == V10PresentationExchange.STATE_REQUEST_RECEIVED - - async def test_create_presentation(self): - exchange_in = V10PresentationExchange() - indy_proof_req = await PRES_PREVIEW.indy_proof_request( - name=PROOF_REQ_NAME, - version=PROOF_REQ_VERSION, - nonce=PROOF_REQ_NONCE, - profile=self.profile, - ) - - exchange_in.presentation_request = indy_proof_req - - more_magic_rr = mock.MagicMock( - get_or_fetch_local_tails_path=mock.CoroutineMock( - return_value="/tmp/sample/tails/path" - ) - ) - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - test_module, "AttachDecorator", autospec=True - ) as mock_attach_decorator, mock.patch.object( - test_indy_util_module, "RevocationRegistry", autospec=True - ) as mock_rr: - mock_rr.from_definition = mock.MagicMock(return_value=more_magic_rr) - - mock_attach_decorator.data_base64 = mock.MagicMock( - return_value=mock_attach_decorator - ) - - req_creds = await indy_proof_req_preview2indy_requested_creds( - indy_proof_req, holder=self.holder - ) - assert not req_creds["self_attested_attributes"] - assert len(req_creds["requested_attributes"]) == 2 - 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_create_presentation_proof_req_non_revoc_interval_none(self): - exchange_in = V10PresentationExchange() - indy_proof_req = await PRES_PREVIEW.indy_proof_request( - name=PROOF_REQ_NAME, - version=PROOF_REQ_VERSION, - nonce=PROOF_REQ_NONCE, - profile=self.profile, - ) - indy_proof_req["non_revoked"] = None # simulate interop with indy-vcx - - exchange_in.presentation_request = indy_proof_req - - more_magic_rr = mock.MagicMock( - get_or_fetch_local_tails_path=mock.CoroutineMock( - return_value="/tmp/sample/tails/path" - ) - ) - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - test_module, "AttachDecorator", autospec=True - ) as mock_attach_decorator, mock.patch.object( - test_indy_util_module, "RevocationRegistry", autospec=True - ) as mock_rr: - mock_rr.from_definition = mock.MagicMock(return_value=more_magic_rr) - - mock_attach_decorator.data_base64 = mock.MagicMock( - return_value=mock_attach_decorator - ) - - req_creds = await indy_proof_req_preview2indy_requested_creds( - indy_proof_req, holder=self.holder - ) - assert not req_creds["self_attested_attributes"] - assert len(req_creds["requested_attributes"]) == 2 - 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_create_presentation_self_asserted(self): - PRES_PREVIEW_SELFIE = IndyPresPreview( - attributes=[ - IndyPresAttrSpec(name="player", value="Richie Knucklez"), - IndyPresAttrSpec( - name="screenCapture", - mime_type="image/png", - value="aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - ), - ], - predicates=[ - IndyPresPredSpec( - name="highScore", - cred_def_id=None, - predicate=">=", - threshold=1000000, - ) - ], - ) - - exchange_in = V10PresentationExchange() - indy_proof_req = await PRES_PREVIEW_SELFIE.indy_proof_request( - name=PROOF_REQ_NAME, - version=PROOF_REQ_VERSION, - nonce=PROOF_REQ_NONCE, - profile=self.profile, - ) - - exchange_in.presentation_request = indy_proof_req - - more_magic_rr = mock.MagicMock( - get_or_fetch_local_tails_path=mock.CoroutineMock( - return_value="/tmp/sample/tails/path" - ) - ) - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - test_module, "AttachDecorator", autospec=True - ) as mock_attach_decorator, mock.patch.object( - test_indy_util_module, "RevocationRegistry", autospec=True - ) as mock_rr: - mock_rr.from_definition = mock.MagicMock(return_value=more_magic_rr) - - mock_attach_decorator.data_base64 = mock.MagicMock( - return_value=mock_attach_decorator - ) - - req_creds = await indy_proof_req_preview2indy_requested_creds( - indy_proof_req, holder=self.holder - ) - assert len(req_creds["self_attested_attributes"]) == 3 - assert not req_creds["requested_attributes"] - assert not req_creds["requested_predicates"] - - (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_create_presentation_no_revocation(self): - Ledger = mock.MagicMock(BaseLedger, autospec=True) - self.ledger = Ledger() - self.ledger.get_schema = mock.CoroutineMock(return_value=mock.MagicMock()) - self.ledger.get_credential_definition = mock.CoroutineMock( - return_value={"value": {"revocation": None}} - ) - self.profile.context.injector.bind_instance(BaseLedger, self.ledger) - - exchange_in = V10PresentationExchange() - indy_proof_req = await PRES_PREVIEW.indy_proof_request( - name=PROOF_REQ_NAME, - version=PROOF_REQ_VERSION, - nonce=PROOF_REQ_NONCE, - profile=self.profile, - ) - - exchange_in.presentation_request = indy_proof_req - - Holder = mock.MagicMock(IndyHolder, autospec=True) - self.holder = Holder() - get_creds = mock.CoroutineMock( - return_value=( - { - "cred_info": {"referent": "dummy_reft"}, - "attrs": { - "player": "Richie Knucklez", - "screenCapture": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - "highScore": "1234560", - }, - }, # leave this comma: return a tuple - ) - ) - self.holder.get_credentials_for_presentation_request_by_referent = get_creds - self.holder.get_credential = mock.CoroutineMock( - return_value=json.dumps( - { - "schema_id": S_ID, - "cred_def_id": CD_ID, - "rev_reg_id": None, - "cred_rev_id": None, - } - ) - ) - self.holder.create_presentation = mock.CoroutineMock(return_value="{}") - self.profile.context.injector.bind_instance(IndyHolder, self.holder) - - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - test_module, "AttachDecorator", autospec=True - ) as mock_attach_decorator, mock.patch.object( - test_indy_util_module.LOGGER, "info", mock.MagicMock() - ) as mock_log_info: - mock_attach_decorator.data_base64 = mock.MagicMock( - return_value=mock_attach_decorator - ) - - req_creds = await indy_proof_req_preview2indy_requested_creds( - indy_proof_req, holder=self.holder - ) - - (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 - - # exercise superfluous timestamp removal - for pred_reft_spec in req_creds["requested_predicates"].values(): - pred_reft_spec["timestamp"] = 1234567890 - await self.manager.create_presentation(exchange_in, req_creds) - mock_log_info.assert_called_once() - - async def test_create_presentation_bad_revoc_state(self): - exchange_in = V10PresentationExchange() - indy_proof_req = await PRES_PREVIEW.indy_proof_request( - name=PROOF_REQ_NAME, - version=PROOF_REQ_VERSION, - nonce=PROOF_REQ_NONCE, - profile=self.profile, - ) - - exchange_in.presentation_request = indy_proof_req - - Holder = mock.MagicMock(IndyHolder, autospec=True) - self.holder = Holder() - get_creds = mock.CoroutineMock( - return_value=( - { - "cred_info": {"referent": "dummy_reft"}, - "attrs": { - "player": "Richie Knucklez", - "screenCapture": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - "highScore": "1234560", - }, - }, # leave this comma: return a tuple - ) - ) - self.holder.get_credentials_for_presentation_request_by_referent = get_creds - - self.holder.get_credential = 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 = mock.CoroutineMock(return_value="{}") - self.holder.create_revocation_state = mock.CoroutineMock( - side_effect=IndyHolderError("Problem", {"message": "Nope"}) - ) - self.profile.context.injector.bind_instance(IndyHolder, self.holder) - - more_magic_rr = mock.MagicMock( - get_or_fetch_local_tails_path=mock.CoroutineMock( - return_value="/tmp/sample/tails/path" - ) - ) - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - test_module, "AttachDecorator", autospec=True - ) as mock_attach_decorator, mock.patch.object( - test_indy_util_module, "RevocationRegistry", autospec=True - ) as mock_rr: - mock_rr.from_definition = mock.MagicMock(return_value=more_magic_rr) - - mock_attach_decorator.data_base64 = mock.MagicMock( - return_value=mock_attach_decorator - ) - - req_creds = await indy_proof_req_preview2indy_requested_creds( - indy_proof_req, holder=self.holder - ) - - with self.assertRaises(IndyHolderError): - await self.manager.create_presentation(exchange_in, req_creds) - - async def test_create_presentation_multi_matching_proposal_creds_names(self): - 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, - profile=self.profile, - ) - - exchange_in.presentation_request = indy_proof_req - - Holder = mock.MagicMock(IndyHolder, autospec=True) - self.holder = Holder() - get_creds = 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 = 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 = mock.CoroutineMock(return_value="{}") - self.holder.create_revocation_state = mock.CoroutineMock( - return_value=json.dumps( - { - "witness": {"omega": "1 ..."}, - "rev_reg": {"accum": "21 ..."}, - "timestamp": NOW, - } - ) - ) - self.profile.context.injector.bind_instance(IndyHolder, self.holder) - - more_magic_rr = mock.MagicMock( - get_or_fetch_local_tails_path=mock.CoroutineMock( - return_value="/tmp/sample/tails/path" - ) - ) - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - test_module, "AttachDecorator", autospec=True - ) as mock_attach_decorator, mock.patch.object( - test_indy_util_module, "RevocationRegistry", autospec=True - ) as mock_rr: - mock_rr.from_definition = mock.MagicMock(return_value=more_magic_rr) - - mock_attach_decorator.data_base64 = 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( - name=PROOF_REQ_NAME, - version=PROOF_REQ_VERSION, - nonce=PROOF_REQ_NONCE, - profile=self.profile, - ) - get_creds = mock.CoroutineMock(return_value=()) - self.holder.get_credentials_for_presentation_request_by_referent = get_creds - - with self.assertRaises(ValueError): - await indy_proof_req_preview2indy_requested_creds( - indy_proof_req, holder=self.holder - ) - - get_creds = mock.CoroutineMock( - return_value=( - { - "cred_info": {"referent": "dummy_reft"}, - "attrs": { - "player": "Richie Knucklez", - "screenCapture": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - "highScore": "1234560", - }, - }, # leave this comma: return a tuple - ) - ) - self.holder.get_credentials_for_presentation_request_by_referent = get_creds - - async def test_receive_presentation(self): - connection_record = mock.MagicMock(connection_id=CONN_ID) - - exchange_dummy = V10PresentationExchange( - presentation_proposal_dict={ - "presentation_proposal": { - "@type": DIDCommPrefix.qualify_current( - "present-proof/1.0/presentation-preview" - ), - "attributes": [ - { - "name": "player", - "cred_def_id": CD_ID, - "value": "Richie Knucklez", - }, - { - "name": "screenCapture", - "cred_def_id": CD_ID, - "value": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - }, - ], - "predicates": [ - { - "name": "highScore", - "cred_def_id": CD_ID, - "predicate": ">=", - "threshold": 1000000, - } - ], - } - }, - presentation_request={ - "name": "proof-request", - "version": "1.0", - "nonce": "1234567890", - "requested_attributes": { - "0_player_uuid": { - "name": "player", - "restrictions": [{"cred_def_id": CD_ID}], - }, - "0_screencapture_uuid": { - "name": "screenCapture", - "restrictions": [{"cred_def_id": CD_ID}], - }, - }, - "requested_predicates": { - "0_highscore_GE_uuid": { - "name": "highScore", - "p_type": ">=", - "p_value": 1000000, - "restrictions": [{"cred_def_id": CD_ID}], - } - }, - }, - presentation={ - "proof": {"proofs": []}, - "requested_proof": { - "revealed_attrs": { - "0_favourite_uuid": { - "sub_proof_index": 0, - "raw": "potato", - "encoded": "12345678901234567890", - }, - "1_icon_uuid": { - "sub_proof_index": 1, - "raw": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - "encoded": "12345678901234567890", - }, - }, - "self_attested_attrs": {}, - "unrevealed_attrs": {}, - "predicates": {}, - }, - "identifiers": [ - { - "schema_id": S_ID, - "cred_def_id": CD_ID, - "rev_reg_id": None, - "timestamp": None, - }, - { - "schema_id": S_ID, - "cred_def_id": CD_ID, - "rev_reg_id": None, - "timestamp": None, - }, - ], - }, - ) - - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10PresentationExchange, "retrieve_by_tag_filter", autospec=True - ) as retrieve_ex, mock.patch.object( - self.profile, - "session", - mock.MagicMock(return_value=self.profile.session()), - ) as session: - retrieve_ex.side_effect = [exchange_dummy] - exchange_out = await self.manager.receive_presentation( - PRES, connection_record, None - ) - retrieve_ex.assert_called_once_with( - session.return_value, - {"thread_id": "dummy"}, - { - "role": V10PresentationExchange.ROLE_VERIFIER, - "connection_id": CONN_ID, - }, - ) - save_ex.assert_called_once() - assert exchange_out.state == ( - V10PresentationExchange.STATE_PRESENTATION_RECEIVED - ) - - async def test_receive_presentation_oob(self): - exchange_dummy = V10PresentationExchange( - presentation_proposal_dict={ - "presentation_proposal": { - "@type": DIDCommPrefix.qualify_current( - "present-proof/1.0/presentation-preview" - ), - "attributes": [ - { - "name": "player", - "cred_def_id": CD_ID, - "value": "Richie Knucklez", - }, - { - "name": "screenCapture", - "cred_def_id": CD_ID, - "value": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - }, - ], - "predicates": [ - { - "name": "highScore", - "cred_def_id": CD_ID, - "predicate": ">=", - "threshold": 1000000, - } - ], - } - }, - presentation_request={ - "name": "proof-request", - "version": "1.0", - "nonce": "1234567890", - "requested_attributes": { - "0_player_uuid": { - "name": "player", - "restrictions": [{"cred_def_id": CD_ID}], - }, - "0_screencapture_uuid": { - "name": "screenCapture", - "restrictions": [{"cred_def_id": CD_ID}], - }, - }, - "requested_predicates": { - "0_highscore_GE_uuid": { - "name": "highScore", - "p_type": ">=", - "p_value": 1000000, - "restrictions": [{"cred_def_id": CD_ID}], - } - }, - }, - presentation={ - "proof": {"proofs": []}, - "requested_proof": { - "revealed_attrs": { - "0_favourite_uuid": { - "sub_proof_index": 0, - "raw": "potato", - "encoded": "12345678901234567890", - }, - "1_icon_uuid": { - "sub_proof_index": 1, - "raw": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - "encoded": "12345678901234567890", - }, - }, - "self_attested_attrs": {}, - "unrevealed_attrs": {}, - "predicates": {}, - }, - "identifiers": [ - { - "schema_id": S_ID, - "cred_def_id": CD_ID, - "rev_reg_id": None, - "timestamp": None, - }, - { - "schema_id": S_ID, - "cred_def_id": CD_ID, - "rev_reg_id": None, - "timestamp": None, - }, - ], - }, - ) - - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10PresentationExchange, "retrieve_by_tag_filter", autospec=True - ) as retrieve_ex, mock.patch.object( - self.profile, - "session", - mock.MagicMock(return_value=self.profile.session()), - ) as session: - retrieve_ex.side_effect = [exchange_dummy] - exchange_out = await self.manager.receive_presentation(PRES, None, None) - retrieve_ex.assert_called_once_with( - session.return_value, - {"thread_id": "dummy"}, - {"role": V10PresentationExchange.ROLE_VERIFIER, "connection_id": None}, - ) - assert exchange_out.state == ( - V10PresentationExchange.STATE_PRESENTATION_RECEIVED - ) - - async def test_receive_presentation_bait_and_switch(self): - connection_record = mock.MagicMock(connection_id=CONN_ID) - - exchange_dummy = V10PresentationExchange( - presentation_proposal_dict={ - "presentation_proposal": { - "@type": DIDCommPrefix.qualify_current( - "present-proof/1.0/presentation-preview" - ), - "attributes": [ - { - "name": "player", - "cred_def_id": CD_ID, - "value": "Richie Knucklez", - }, - { - "name": "screenCapture", - "cred_def_id": CD_ID, - "value": "YSBwaWN0dXJlIG9mIGEgcG90YXRv", - }, - ], - "predicates": [ - { - "name": "highScore", - "cred_def_id": CD_ID, - "predicate": ">=", - "threshold": 1000000, - } - ], - } - }, - presentation_request={ - "name": "proof-request", - "version": "1.0", - "nonce": "1234567890", - "requested_attributes": { - "0_player_uuid": { - "name": "player", - "restrictions": [{"cred_def_id": CD_ID}], - }, - "0_screencapture_uuid": { - "name": "screenCapture", - "restrictions": [{"cred_def_id": CD_ID}], - }, - }, - "requested_predicates": { - "0_highscore_GE_uuid": { - "name": "highScore", - "p_type": ">=", - "p_value": 1000000, - "restrictions": [{"cred_def_id": CD_ID}], - } - }, - }, - presentation={ - "proof": {"proofs": []}, - "requested_proof": { - "revealed_attrs": { - "0_favourite_uuid": { - "sub_proof_index": 0, - "raw": "potato", - "encoded": "12345678901234567890", - }, - "1_icon_uuid": { - "sub_proof_index": 1, - "raw": "aW1hZ2luZSBhIHNjcmVlbiBjYXB0dXJl", - "encoded": "12345678901234567890", - }, - }, - "self_attested_attrs": {}, - "unrevealed_attrs": {}, - "predicates": {}, - }, - "identifiers": [ - { - "schema_id": S_ID, - "cred_def_id": CD_ID, - "rev_reg_id": None, - "timestamp": None, - }, - { - "schema_id": S_ID, - "cred_def_id": CD_ID, - "rev_reg_id": None, - "timestamp": None, - }, - ], - }, - ) - - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10PresentationExchange, "retrieve_by_tag_filter", autospec=True - ) as retrieve_ex: - retrieve_ex.return_value = exchange_dummy - with self.assertRaises(PresentationManagerError): - await self.manager.receive_presentation(PRES, connection_record, None) - - async def test_receive_presentation_connectionless(self): - exchange_dummy = V10PresentationExchange() - - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10PresentationExchange, "retrieve_by_tag_filter", autospec=True - ) as retrieve_ex, mock.patch.object( - self.profile, - "session", - mock.MagicMock(return_value=self.profile.session()), - ) as session: - retrieve_ex.return_value = exchange_dummy - exchange_out = await self.manager.receive_presentation(PRES, None, None) - retrieve_ex.assert_called_once_with( - session.return_value, - {"thread_id": PRES._thread_id}, - {"role": V10PresentationExchange.ROLE_VERIFIER, "connection_id": None}, - ) - save_ex.assert_called_once() - - assert exchange_out.state == ( - V10PresentationExchange.STATE_PRESENTATION_RECEIVED - ) - - async def test_verify_presentation(self): - indy_proof_req = await PRES_PREVIEW.indy_proof_request( - name=PROOF_REQ_NAME, - version=PROOF_REQ_VERSION, - nonce=PROOF_REQ_NONCE, - profile=self.profile, - ) - pres_req = PresentationRequest( - request_presentations_attach=[ - AttachDecorator.data_base64( - mapping=indy_proof_req, - ident=ATTACH_DECO_IDS[PRESENTATION_REQUEST], - ) - ] - ) - exchange_in = V10PresentationExchange( - presentation_exchange_id="dummy-pxid", - connection_id="dummy-conn-id", - initiator=V10PresentationExchange.INITIATOR_SELF, - role=V10PresentationExchange.ROLE_VERIFIER, - presentation_request=pres_req, - presentation=INDY_PROOF, - ) - - with mock.patch.object(V10PresentationExchange, "save", autospec=True) as save_ex: - exchange_out = await self.manager.verify_presentation(exchange_in) - save_ex.assert_called_once() - - assert exchange_out.state == (V10PresentationExchange.STATE_VERIFIED) - - """ - async def test_verify_presentation_with_revocation(self): - exchange_in = V10PresentationExchange() - exchange_in.presentation = { - "identifiers": [ - { - "schema_id": S_ID, - "cred_def_id": CD_ID, - "rev_reg_id": RR_ID, - "timestamp": NOW, - }, - { # cover multiple instances of same rev reg - "schema_id": S_ID, - "cred_def_id": CD_ID, - "rev_reg_id": RR_ID, - "timestamp": NOW, - }, - ] - } - - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex: - exchange_out = await self.manager.verify_presentation(exchange_in) - save_ex.assert_called_once() - - assert exchange_out.state == (V10PresentationExchange.STATE_VERIFIED) - """ - - async def test_send_presentation_ack(self): - exchange = V10PresentationExchange(connection_id="dummy") - - responder = MockResponder() - self.profile.context.injector.bind_instance(BaseResponder, responder) - - await self.manager.send_presentation_ack(exchange) - messages = responder.messages - assert len(messages) == 1 - - async def test_send_presentation_ack_oob(self): - exchange = V10PresentationExchange(thread_id="some-thread-id") - - responder = MockResponder() - self.profile.context.injector.bind_instance(BaseResponder, responder) - - with mock.patch.object( - test_module.OobRecord, "retrieve_by_tag_filter" - ) as mock_retrieve_oob, mock.patch.object( - self.profile, - "session", - mock.MagicMock(return_value=self.profile.session()), - ) as session: - await self.manager.send_presentation_ack(exchange) - messages = responder.messages - mock_retrieve_oob.assert_called_once_with( - session.return_value, {"attach_thread_id": "some-thread-id"} - ) - assert len(messages) == 1 - - async def test_send_presentation_ack_no_responder(self): - exchange = V10PresentationExchange() - - self.profile.context.injector.clear_binding(BaseResponder) - await self.manager.send_presentation_ack(exchange) - - async def test_receive_presentation_ack_a(self): - connection_record = mock.MagicMock(connection_id=CONN_ID) - - exchange_dummy = V10PresentationExchange() - message = mock.MagicMock() - - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10PresentationExchange, "retrieve_by_tag_filter", autospec=True - ) as retrieve_ex: - retrieve_ex.return_value = exchange_dummy - exchange_out = await self.manager.receive_presentation_ack( - message, connection_record - ) - save_ex.assert_called_once() - - assert exchange_out.state == ( - V10PresentationExchange.STATE_PRESENTATION_ACKED - ) - - async def test_receive_presentation_ack_b(self): - connection_record = mock.MagicMock(connection_id=CONN_ID) - - exchange_dummy = V10PresentationExchange() - message = mock.MagicMock(_verification_result="true") - - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10PresentationExchange, "retrieve_by_tag_filter", autospec=True - ) as retrieve_ex: - retrieve_ex.return_value = exchange_dummy - exchange_out = await self.manager.receive_presentation_ack( - message, connection_record - ) - save_ex.assert_called_once() - - assert exchange_out.state == ( - V10PresentationExchange.STATE_PRESENTATION_ACKED - ) - assert exchange_out.verified == "true" - - async def test_receive_problem_report(self): - connection_id = "connection-id" - stored_exchange = V10PresentationExchange( - presentation_exchange_id="dummy-pxid", - connection_id=connection_id, - initiator=V10PresentationExchange.INITIATOR_SELF, - role=V10PresentationExchange.ROLE_VERIFIER, - state=V10PresentationExchange.STATE_PROPOSAL_RECEIVED, - thread_id="dummy-thid", - ) - problem = PresentationProblemReport( - description={ - "code": test_module.ProblemReportReason.ABANDONED.value, - "en": "Change of plans", - } - ) - - with mock.patch.object( - V10PresentationExchange, "save", autospec=True - ) as save_ex, mock.patch.object( - V10PresentationExchange, - "retrieve_by_tag_filter", - mock.CoroutineMock(), - ) as retrieve_ex, mock.patch.object( - self.profile, - "session", - mock.MagicMock(return_value=self.profile.session()), - ) as session: - retrieve_ex.return_value = stored_exchange - - ret_exchange = await self.manager.receive_problem_report( - problem, connection_id - ) - retrieve_ex.assert_called_once_with( - session.return_value, - {"thread_id": problem._thread_id}, - {"connection_id": connection_id}, - ) - save_ex.assert_called_once() - - assert ret_exchange.state == V10CredentialExchange.STATE_ABANDONED - - async def test_receive_problem_report_x(self): - connection_id = "connection-id" - stored_exchange = V10PresentationExchange( - presentation_exchange_id="dummy-pxid", - connection_id=connection_id, - initiator=V10PresentationExchange.INITIATOR_SELF, - role=V10PresentationExchange.ROLE_VERIFIER, - state=V10PresentationExchange.STATE_PROPOSAL_RECEIVED, - thread_id="dummy-thid", - ) - problem = PresentationProblemReport( - description={ - "code": test_module.ProblemReportReason.ABANDONED.value, - "en": "Change of plans", - } - ) - - with mock.patch.object( - V10PresentationExchange, - "retrieve_by_tag_filter", - mock.CoroutineMock(), - ) as retrieve_ex: - retrieve_ex.side_effect = test_module.StorageNotFoundError("No such record") - - with self.assertRaises(test_module.StorageNotFoundError): - await self.manager.receive_problem_report(problem, connection_id) diff --git a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_routes.py b/aries_cloudagent/protocols/present_proof/v1_0/tests/test_routes.py deleted file mode 100644 index 0f20eae8e4..0000000000 --- a/aries_cloudagent/protocols/present_proof/v1_0/tests/test_routes.py +++ /dev/null @@ -1,1498 +0,0 @@ -import importlib -from unittest import IsolatedAsyncioTestCase - -from marshmallow import ValidationError - -from aries_cloudagent.tests import mock - -from .....admin.request_context import AdminRequestContext -from .....core.in_memory import InMemoryProfile -from .....indy.holder import IndyHolder -from .....indy.models.proof_request import IndyProofReqAttrSpecSchema -from .....indy.verifier import IndyVerifier -from .....ledger.base import BaseLedger -from .....storage.error import StorageNotFoundError -from .. import routes as test_module - - -class TestProofRoutes(IsolatedAsyncioTestCase): - def setUp(self): - profile = InMemoryProfile.test_profile( - settings={ - "admin.admin_api_key": "secret-key", - } - ) - self.context = AdminRequestContext.test_context(profile=profile) - self.profile = self.context.profile - self.request_dict = { - "context": self.context, - "outbound_message_router": mock.CoroutineMock(), - } - self.request = mock.MagicMock( - app={}, - match_info={}, - query={}, - __getitem__=lambda _, k: self.request_dict[k], - headers={"x-api-key": "secret-key"}, - ) - - async def test_validate_proof_req_attr_spec(self): - aspec = IndyProofReqAttrSpecSchema() - aspec.validate_fields({"name": "attr0"}) - aspec.validate_fields( - { - "names": ["attr0", "attr1"], - "restrictions": [{"attr::attr1::value": "my-value"}], - } - ) - aspec.validate_fields( - {"name": "attr0", "restrictions": [{"schema_name": "preferences"}]} - ) - with self.assertRaises(ValidationError): - aspec.validate_fields({}) - with self.assertRaises(ValidationError): - aspec.validate_fields({"name": "attr0", "names": ["attr1", "attr2"]}) - with self.assertRaises(ValidationError): - aspec.validate_fields({"names": ["attr1", "attr2"]}) - with self.assertRaises(ValidationError): - aspec.validate_fields({"names": ["attr0", "attr1"], "restrictions": []}) - with self.assertRaises(ValidationError): - aspec.validate_fields({"names": ["attr0", "attr1"], "restrictions": [{}]}) - - async def test_presentation_exchange_list(self): - self.request.query = { - "thread_id": "thread_id_0", - "connection_id": "conn_id_0", - "role": "dummy", - "state": "dummy", - } - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.query = mock.CoroutineMock() - mock_presentation_exchange.query.return_value = [mock_presentation_exchange] - mock_presentation_exchange.serialize = mock.MagicMock() - mock_presentation_exchange.serialize.return_value = { - "thread_id": "sample-thread-id" - } - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.presentation_exchange_list(self.request) - mock_response.assert_called_once_with( - {"results": [mock_presentation_exchange.serialize.return_value]} - ) - - async def test_presentation_exchange_list_x(self): - self.request.query = { - "thread_id": "thread_id_0", - "connection_id": "conn_id_0", - "role": "dummy", - "state": "dummy", - } - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.query = mock.CoroutineMock( - side_effect=test_module.StorageError() - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_list(self.request) - - async def test_presentation_exchange_credentials_list_not_found(self): - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock() - - # Emulate storage not found (bad presentation exchange id) - mock_presentation_exchange.retrieve_by_id.side_effect = StorageNotFoundError - - with self.assertRaises(test_module.web.HTTPNotFound): - await test_module.presentation_exchange_credentials_list(self.request) - - async def test_presentation_exchange_credentials_x(self): - self.request.match_info = { - "pres_ex_id": "123-456-789", - "referent": "myReferent1", - } - self.request.query = {"extra_query": {}} - returned_credentials = [{"name": "Credential1"}, {"name": "Credential2"}] - self.profile.context.injector.bind_instance( - IndyHolder, - mock.MagicMock( - get_credentials_for_presentation_request_by_referent=( - mock.CoroutineMock(side_effect=test_module.IndyHolderError()) - ) - ), - ) - mock_px_rec = mock.MagicMock(save_error_state=mock.CoroutineMock()) - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id.return_value = mock_px_rec - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_credentials_list(self.request) - - async def test_presentation_exchange_credentials_list_single_referent(self): - self.request.match_info = { - "pres_ex_id": "123-456-789", - "referent": "myReferent1", - } - self.request.query = {"extra_query": {}} - - returned_credentials = [{"name": "Credential1"}, {"name": "Credential2"}] - self.profile.context.injector.bind_instance( - IndyHolder, - mock.MagicMock( - get_credentials_for_presentation_request_by_referent=mock.CoroutineMock( - return_value=returned_credentials - ) - ), - ) - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id.return_value = mock.MagicMock() - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.presentation_exchange_credentials_list(self.request) - mock_response.assert_called_once_with(returned_credentials) - - async def test_presentation_exchange_credentials_list_multiple_referents(self): - self.request.match_info = { - "pres_ex_id": "123-456-789", - "referent": "myReferent1,myReferent2", - } - self.request.query = {"extra_query": {}} - - returned_credentials = [{"name": "Credential1"}, {"name": "Credential2"}] - self.profile.context.injector.bind_instance( - IndyHolder, - mock.MagicMock( - get_credentials_for_presentation_request_by_referent=( - mock.CoroutineMock(return_value=returned_credentials) - ) - ), - ) - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock() - ) - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.presentation_exchange_credentials_list(self.request) - mock_response.assert_called_once_with(returned_credentials) - - async def test_presentation_exchange_retrieve(self): - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_pres_ex: - # Since we are mocking import - importlib.reload(test_module) - - mock_pres_ex.retrieve_by_id = mock.CoroutineMock() - mock_pres_ex.retrieve_by_id.return_value = mock_pres_ex - mock_pres_ex.serialize = mock.MagicMock() - mock_pres_ex.serialize.return_value = {"thread_id": "sample-thread-id"} - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.presentation_exchange_retrieve(self.request) - mock_response.assert_called_once_with(mock_pres_ex.serialize.return_value) - - async def test_presentation_exchange_retrieve_not_found(self): - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_pres_ex: - # Since we are mocking import - importlib.reload(test_module) - - mock_pres_ex.retrieve_by_id = mock.CoroutineMock() - - # Emulate storage not found (bad presentation exchange id) - mock_pres_ex.retrieve_by_id.side_effect = StorageNotFoundError - - with self.assertRaises(test_module.web.HTTPNotFound): - await test_module.presentation_exchange_retrieve(self.request) - - async def test_presentation_exchange_retrieve_x(self): - self.request.match_info = {"pres_ex_id": "dummy"} - - mock_pres_ex_rec = mock.MagicMock( - connection_id="abc123", - thread_id="thid123", - save_error_state=mock.CoroutineMock(), - ) - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_pres_ex: - # Since we are mocking import - importlib.reload(test_module) - - mock_pres_ex.retrieve_by_id = mock.CoroutineMock( - return_value=mock_pres_ex_rec - ) - mock_pres_ex_rec.serialize = mock.MagicMock( - side_effect=test_module.BaseModelError() - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_retrieve(self.request) - - async def test_presentation_exchange_send_proposal(self): - self.request.json = mock.CoroutineMock() - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.models.pres_preview.IndyPresPreview", - autospec=True, - ) as mock_preview: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange_record = mock.MagicMock() - mock_presentation_manager.return_value.create_exchange_for_proposal = ( - mock.CoroutineMock(return_value=mock_presentation_exchange_record) - ) - - mock_preview.return_value.deserialize.return_value = mock.MagicMock() - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.presentation_exchange_send_proposal(self.request) - mock_response.assert_called_once_with( - mock_presentation_exchange_record.serialize.return_value - ) - - async def test_presentation_exchange_send_proposal_no_conn_record(self): - self.request.json = mock.CoroutineMock() - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record: - # Since we are mocking import - importlib.reload(test_module) - - # Emulate storage not found (bad connection id) - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - side_effect=StorageNotFoundError - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_send_proposal(self.request) - - async def test_presentation_exchange_send_proposal_not_ready(self): - self.request.json = mock.CoroutineMock() - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.indy.models.pres_preview.IndyPresPreview", - autospec=True, - ) as mock_preview, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "messages.presentation_proposal.PresentationProposal" - ), - autospec=True, - ) as mock_proposal: - # Since we are mocking import - importlib.reload(test_module) - - mock_connection_record.retrieve_by_id = mock.CoroutineMock() - mock_connection_record.retrieve_by_id.return_value.is_ready = False - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.presentation_exchange_send_proposal(self.request) - - async def test_presentation_exchange_send_proposal_x(self): - self.request.json = mock.CoroutineMock() - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.models.pres_preview.IndyPresPreview", - autospec=True, - ) as mock_preview: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange_record = mock.MagicMock() - mock_presentation_manager.return_value.create_exchange_for_proposal = ( - mock.CoroutineMock( - return_value=mock.MagicMock( - serialize=mock.MagicMock(side_effect=test_module.StorageError()), - save_error_state=mock.CoroutineMock(), - ) - ) - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_send_proposal(self.request) - - async def test_presentation_exchange_create_request(self): - self.request.json = mock.CoroutineMock( - return_value={"comment": "dummy", "proof_request": {}} - ) - - with mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.models.pres_preview.IndyPresPreview", - autospec=True, - ) as mock_preview, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce: - # Since we are mocking import - importlib.reload(test_module) - - mock_generate_nonce = mock.CoroutineMock() - - mock_attach_decorator.data_base64 = mock.MagicMock( - return_value=mock_attach_decorator - ) - mock_presentation_exchange.serialize = mock.MagicMock() - mock_presentation_exchange.serialize.return_value = { - "thread_id": "sample-thread-id" - } - mock_mgr = mock.MagicMock( - create_exchange_for_request=mock.CoroutineMock( - return_value=mock_presentation_exchange - ) - ) - mock_presentation_manager.return_value = mock_mgr - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.presentation_exchange_create_request(self.request) - mock_response.assert_called_once_with( - mock_presentation_exchange.serialize.return_value - ) - - async def test_presentation_exchange_create_request_x(self): - self.request.json = mock.CoroutineMock( - return_value={"comment": "dummy", "proof_request": {}} - ) - - with mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.models.pres_preview.IndyPresPreview", - autospec=True, - ) as mock_preview, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange_record = mock.MagicMock() - mock_presentation_manager.return_value.create_exchange_for_request = ( - mock.CoroutineMock( - return_value=mock.MagicMock( - serialize=mock.MagicMock(side_effect=test_module.StorageError()), - save_error_state=mock.CoroutineMock(), - ) - ) - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_create_request(self.request) - - async def test_presentation_exchange_send_free_request(self): - self.request.json = mock.CoroutineMock( - return_value={ - "connection_id": "dummy", - "comment": "dummy", - "proof_request": {}, - } - ) - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce, mock.patch( - "aries_cloudagent.indy.models.pres_preview.IndyPresPreview", - autospec=True, - ) as mock_preview, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - return_value=mock_connection_record - ) - mock_attach_decorator.data_base64 = mock.MagicMock( - return_value=mock_attach_decorator - ) - mock_presentation_exchange.serialize = mock.MagicMock() - mock_presentation_exchange.serialize.return_value = { - "thread_id": "sample-thread-id" - } - - mock_mgr = mock.MagicMock( - create_exchange_for_request=mock.CoroutineMock( - return_value=mock_presentation_exchange - ) - ) - mock_presentation_manager.return_value = mock_mgr - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.presentation_exchange_send_free_request(self.request) - mock_response.assert_called_once_with( - mock_presentation_exchange.serialize.return_value - ) - - async def test_presentation_exchange_send_free_request_not_found(self): - self.request.json = mock.CoroutineMock(return_value={"connection_id": "dummy"}) - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record: - # Since we are mocking import - importlib.reload(test_module) - - mock_connection_record.retrieve_by_id = mock.CoroutineMock() - mock_connection_record.retrieve_by_id.side_effect = StorageNotFoundError - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_send_free_request(self.request) - - async def test_presentation_exchange_send_free_request_not_ready(self): - self.request.json = mock.CoroutineMock( - return_value={"connection_id": "dummy", "proof_request": {}} - ) - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record: - # Since we are mocking import - importlib.reload(test_module) - - mock_connection_record.is_ready = False - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - return_value=mock_connection_record - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.presentation_exchange_send_free_request(self.request) - - async def test_presentation_exchange_send_free_request_x(self): - self.request.json = mock.CoroutineMock( - return_value={ - "connection_id": "dummy", - "comment": "dummy", - "proof_request": {}, - } - ) - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce, mock.patch.object( - test_module, "IndyPresPreview", autospec=True - ) as mock_presentation_proposal, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_generate_nonce = mock.CoroutineMock() - - mock_presentation_exchange_record = mock.MagicMock() - mock_presentation_manager.return_value.create_exchange_for_request = ( - mock.CoroutineMock( - return_value=mock.MagicMock( - serialize=mock.MagicMock(side_effect=test_module.StorageError()), - save_error_state=mock.CoroutineMock(), - ) - ) - ) - - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - return_value=mock_connection_record - ) - mock_attach_decorator.data_base64 = mock.MagicMock( - return_value=mock_attach_decorator - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_send_free_request(self.request) - - async def test_presentation_exchange_send_bound_request(self): - self.request.json = mock.CoroutineMock(return_value={"trace": False}) - self.request.match_info = {"pres_ex_id": "dummy"} - - self.profile.context.injector.bind_instance( - BaseLedger, - mock.MagicMock( - __aenter__=mock.CoroutineMock(), - __aexit__=mock.CoroutineMock(), - ), - ) - self.profile.context.injector.bind_instance( - IndyVerifier, - mock.MagicMock( - verify_presentation=mock.CoroutineMock(), - ), - ) - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce, mock.patch.object( - test_module, "IndyPresPreview", autospec=True - ) as mock_presentation_proposal, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange", - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.connection_id = "dummy" - mock_presentation_exchange.state = ( - test_module.V10PresentationExchange.STATE_PROPOSAL_RECEIVED - ) - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock_presentation_exchange - ) - mock_presentation_exchange.serialize = mock.MagicMock() - mock_presentation_exchange.serialize.return_value = { - "thread_id": "sample-thread-id" - } - mock_connection_record.is_ready = True - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - return_value=mock_connection_record - ) - - mock_mgr = mock.MagicMock( - create_bound_request=mock.CoroutineMock( - return_value=(mock_presentation_exchange, mock_presentation_request) - ) - ) - mock_presentation_manager.return_value = mock_mgr - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.presentation_exchange_send_bound_request(self.request) - mock_response.assert_called_once_with( - mock_presentation_exchange.serialize.return_value - ) - - async def test_presentation_exchange_send_bound_request_not_found(self): - self.request.json = mock.CoroutineMock(return_value={"trace": False}) - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce, mock.patch.object( - test_module, "IndyPresPreview", autospec=True - ) as mock_presentation_proposal, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.connection_id = "dummy" - mock_presentation_exchange.state = ( - test_module.V10PresentationExchange.STATE_PROPOSAL_RECEIVED - ) - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock_presentation_exchange - ) - - mock_connection_record.retrieve_by_id = mock.CoroutineMock() - mock_connection_record.retrieve_by_id.side_effect = StorageNotFoundError - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_send_bound_request(self.request) - - async def test_presentation_exchange_send_bound_request_not_ready(self): - self.request.json = mock.CoroutineMock(return_value={"trace": False}) - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce, mock.patch.object( - test_module, "IndyPresPreview", autospec=True - ) as mock_presentation_proposal, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.connection_id = "dummy" - mock_presentation_exchange.state = ( - test_module.V10PresentationExchange.STATE_PROPOSAL_RECEIVED - ) - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock_presentation_exchange - ) - - mock_connection_record.is_ready = False - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - return_value=mock_connection_record - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.presentation_exchange_send_bound_request(self.request) - - async def test_presentation_exchange_send_bound_request_px_rec_not_found(self): - self.request.json = mock.CoroutineMock(return_value={"trace": False}) - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch.object( - test_module.V10PresentationExchange, - "retrieve_by_id", - mock.CoroutineMock(), - ) as mock_retrieve: - mock_retrieve.side_effect = StorageNotFoundError("no such record") - with self.assertRaises(test_module.web.HTTPNotFound) as context: - await test_module.presentation_exchange_send_bound_request(self.request) - assert "no such record" in str(context.exception) - - async def test_presentation_exchange_send_bound_request_bad_state(self): - self.request.json = mock.CoroutineMock(return_value={"trace": False}) - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.connection_id = "dummy" - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_presentation_exchange.STATE_PRESENTATION_ACKED - ) - ) - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_send_bound_request(self.request) - - async def test_presentation_exchange_send_bound_request_x(self): - self.request.json = mock.CoroutineMock(return_value={"trace": False}) - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce, mock.patch.object( - test_module, "IndyPresPreview", autospec=True - ) as mock_presentation_proposal, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.connection_id = "dummy" - mock_presentation_exchange.state = ( - test_module.V10PresentationExchange.STATE_PROPOSAL_RECEIVED - ) - mock_presentation_exchange.connection_id = "abc123" - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock_presentation_exchange - ) - mock_presentation_exchange.serialize = mock.MagicMock() - mock_presentation_exchange.serialize.return_value = { - "thread_id": "sample-thread-id", - } - mock_connection_record.is_ready = True - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - return_value=mock_connection_record - ) - - mock_mgr = mock.MagicMock( - create_bound_request=mock.CoroutineMock( - side_effect=[ - test_module.LedgerError(), - test_module.StorageError(), - ] - ) - ) - mock_presentation_manager.return_value = mock_mgr - - with self.assertRaises(test_module.web.HTTPBadRequest): # ledger error - await test_module.presentation_exchange_send_bound_request(self.request) - with self.assertRaises(test_module.web.HTTPBadRequest): # storage error - await test_module.presentation_exchange_send_bound_request(self.request) - - async def test_presentation_exchange_send_presentation(self): - self.request.json = mock.CoroutineMock( - return_value={ - "comment": "dummy", - "self_attested_attributes": {}, - "requested_attributes": {}, - "requested_predicates": {}, - } - ) - self.request.match_info = {"pres_ex_id": "dummy"} - self.profile.context.injector.bind_instance( - BaseLedger, - mock.MagicMock( - __aenter__=mock.CoroutineMock(), - __aexit__=mock.CoroutineMock(), - ), - ) - self.profile.context.injector.bind_instance( - IndyVerifier, - mock.MagicMock( - verify_presentation=mock.CoroutineMock(), - ), - ) - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch.object( - test_module, "IndyPresPreview", autospec=True - ) as mock_presentation_proposal, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.state = ( - test_module.V10PresentationExchange.STATE_REQUEST_RECEIVED - ) - mock_presentation_exchange.connection_id = "dummy" - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_presentation_exchange.STATE_REQUEST_RECEIVED, - connection_id="dummy", - serialize=mock.MagicMock( - return_value={"thread_id": "sample-thread-id"} - ), - ) - ) - mock_connection_record.is_ready = True - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - return_value=mock_connection_record - ) - mock_mgr = mock.MagicMock( - create_presentation=mock.CoroutineMock( - return_value=(mock_presentation_exchange, mock.MagicMock()) - ) - ) - mock_presentation_manager.return_value = mock_mgr - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.presentation_exchange_send_presentation(self.request) - mock_response.assert_called_once_with( - mock_presentation_exchange.serialize.return_value - ) - - async def test_presentation_exchange_send_presentation_px_rec_not_found(self): - self.request.json = mock.CoroutineMock(return_value={"trace": False}) - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch.object( - test_module.V10PresentationExchange, - "retrieve_by_id", - mock.CoroutineMock(), - ) as mock_retrieve: - mock_retrieve.side_effect = StorageNotFoundError("no such record") - with self.assertRaises(test_module.web.HTTPNotFound) as context: - await test_module.presentation_exchange_send_presentation(self.request) - assert "no such record" in str(context.exception) - - async def test_presentation_exchange_send_presentation_not_found(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce, mock.patch.object( - test_module, "IndyPresPreview", autospec=True - ) as mock_presentation_proposal, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_presentation_exchange.STATE_REQUEST_RECEIVED, - connection_id="dummy", - ) - ) - - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - side_effect=StorageNotFoundError - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_send_presentation(self.request) - - async def test_presentation_exchange_send_presentation_not_ready(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce, mock.patch.object( - test_module, "IndyPresPreview", autospec=True - ) as mock_presentation_proposal, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_presentation_exchange.STATE_REQUEST_RECEIVED, - connection_id="dummy", - ) - ) - - mock_connection_record.is_ready = False - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - return_value=mock_connection_record - ) - - with self.assertRaises(test_module.web.HTTPForbidden): - await test_module.presentation_exchange_send_presentation(self.request) - - async def test_presentation_exchange_send_presentation_bad_state(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_presentation_exchange.STATE_PRESENTATION_ACKED - ) - ) - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_send_presentation(self.request) - - async def test_presentation_exchange_send_presentation_x(self): - self.request.json = mock.CoroutineMock( - return_value={ - "comment": "dummy", - "self_attested_attributes": {}, - "requested_attributes": {}, - "requested_predicates": {}, - } - ) - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce, mock.patch.object( - test_module, "IndyPresPreview", autospec=True - ) as mock_presentation_proposal, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_presentation_exchange.STATE_REQUEST_RECEIVED, - connection_id="dummy", - serialize=mock.MagicMock( - return_value={"thread_id": "sample-thread-id"} - ), - save_error_state=mock.CoroutineMock(), - ), - ) - mock_connection_record.is_ready = True - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - return_value=mock_connection_record - ) - mock_mgr = mock.MagicMock( - create_presentation=mock.CoroutineMock( - side_effect=test_module.LedgerError() - ) - ) - mock_presentation_manager.return_value = mock_mgr - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_send_presentation(self.request) - - async def test_presentation_exchange_verify_presentation(self): - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - "aries_cloudagent.indy.util.generate_pr_nonce", - autospec=True, - ) as mock_generate_nonce, mock.patch( - "aries_cloudagent.indy.models.pres_preview.IndyPresPreview", - autospec=True, - ) as mock_preview, mock.patch.object( - test_module, "PresentationRequest", autospec=True - ) as mock_presentation_request, mock.patch( - "aries_cloudagent.messaging.decorators.attach_decorator.AttachDecorator", - autospec=True, - ) as mock_attach_decorator, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_presentation_exchange.STATE_PRESENTATION_RECEIVED, - connection_id="dummy", - thread_id="dummy", - serialize=mock.MagicMock( - return_value={"thread_id": "sample-thread-id"} - ), - ) - ) - mock_connection_record.is_ready = True - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - return_value=mock_connection_record - ) - mock_mgr = mock.MagicMock( - verify_presentation=mock.CoroutineMock( - return_value=mock_presentation_exchange.retrieve_by_id.return_value - ) - ) - mock_presentation_manager.return_value = mock_mgr - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.presentation_exchange_verify_presentation(self.request) - mock_response.assert_called_once_with({"thread_id": "sample-thread-id"}) - - async def test_presentation_exchange_verify_presentation_px_rec_not_found(self): - self.request.json = mock.CoroutineMock(return_value={"trace": False}) - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch.object( - test_module.V10PresentationExchange, - "retrieve_by_id", - mock.CoroutineMock(), - ) as mock_retrieve: - mock_retrieve.side_effect = StorageNotFoundError("no such record") - with self.assertRaises(test_module.web.HTTPNotFound) as context: - await test_module.presentation_exchange_verify_presentation(self.request) - assert "no such record" in str(context.exception) - - async def test_presentation_exchange_verify_presentation_bad_state(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_presentation_exchange.STATE_PRESENTATION_ACKED - ) - ) - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_verify_presentation(self.request) - - async def test_presentation_exchange_verify_presentation_x(self): - self.request.match_info = {"pres_ex_id": "dummy"} - self.profile.context.injector.bind_instance( - BaseLedger, - mock.MagicMock( - __aenter__=mock.CoroutineMock(), - __aexit__=mock.CoroutineMock(), - ), - ) - self.profile.context.injector.bind_instance( - IndyVerifier, - mock.MagicMock( - verify_presentation=mock.CoroutineMock(), - ), - ) - - with mock.patch( - "aries_cloudagent.connections.models.conn_record.ConnRecord", - autospec=True, - ) as mock_connection_record, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_presentation_manager, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_presentation_exchange.STATE_PRESENTATION_RECEIVED, - connection_id="dummy", - thread_id="dummy", - serialize=mock.MagicMock( - return_value={"thread_id": "sample-thread-id"} - ), - save_error_state=mock.CoroutineMock(), - ) - ) - - mock_connection_record.is_ready = True - mock_connection_record.retrieve_by_id = mock.CoroutineMock( - return_value=mock_connection_record - ) - mock_mgr = mock.MagicMock( - verify_presentation=mock.CoroutineMock( - side_effect=[ - test_module.LedgerError(), - test_module.StorageError(), - ] - ), - ) - mock_presentation_manager.return_value = mock_mgr - - with self.assertRaises(test_module.web.HTTPBadRequest): # ledger error - await test_module.presentation_exchange_verify_presentation(self.request) - with self.assertRaises(test_module.web.HTTPBadRequest): # storage error - await test_module.presentation_exchange_verify_presentation(self.request) - - async def test_presentation_exchange_problem_report(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"pres_ex_id": "dummy"} - magic_report = mock.MagicMock() - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_pres_ex, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_pres_mgr_cls, mock.patch.object( - test_module, "problem_report_for_record", mock.MagicMock() - ) as mock_problem_report, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - # Since we are mocking import - importlib.reload(test_module) - - mock_pres_ex.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock(save_error_state=mock.CoroutineMock()) - ) - mock_problem_report.return_value = magic_report - - await test_module.presentation_exchange_problem_report(self.request) - - self.request["outbound_message_router"].assert_awaited_once() - mock_response.assert_called_once_with({}) - - async def test_presentation_exchange_problem_report_bad_pres_ex_id(self): - self.request.json = mock.CoroutineMock( - return_value={"description": "Did I say no problem? I meant 'no: problem.'"} - ) - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_pres_mgr_cls, mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_pres_ex: - # Since we are mocking import - importlib.reload(test_module) - - mock_pres_ex.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageNotFoundError() - ) - - with self.assertRaises(test_module.web.HTTPNotFound): - await test_module.presentation_exchange_problem_report(self.request) - - async def test_presentation_exchange_problem_report_x(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"pres_ex_id": "dummy"} - magic_report = mock.MagicMock() - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_pres_ex, mock.patch( - "aries_cloudagent.protocols.present_proof.v1_0.manager.PresentationManager", - autospec=True, - ) as mock_pres_mgr_cls, mock.patch.object( - test_module, "problem_report_for_record", mock.MagicMock() - ) as mock_problem_report, mock.patch.object( - test_module.web, "json_response" - ) as mock_response: - # Since we are mocking import - importlib.reload(test_module) - mock_pres_ex.retrieve_by_id = mock.CoroutineMock( - side_effect=test_module.StorageError() - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_problem_report(self.request) - - async def test_presentation_exchange_remove(self): - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_presentation_exchange.STATE_VERIFIED, - connection_id="dummy", - delete_record=mock.CoroutineMock(), - ) - ) - - with mock.patch.object(test_module.web, "json_response") as mock_response: - await test_module.presentation_exchange_remove(self.request) - mock_response.assert_called_once_with({}) - - async def test_presentation_exchange_remove_not_found(self): - self.request.json = mock.CoroutineMock() - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - # Emulate storage not found (bad pres ex id) - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - side_effect=StorageNotFoundError - ) - - with self.assertRaises(test_module.web.HTTPNotFound): - await test_module.presentation_exchange_remove(self.request) - - async def test_presentation_exchange_remove_x(self): - self.request.match_info = {"pres_ex_id": "dummy"} - - with mock.patch( - ( - "aries_cloudagent.protocols.present_proof.v1_0." - "models.presentation_exchange.V10PresentationExchange" - ), - autospec=True, - ) as mock_presentation_exchange: - # Since we are mocking import - importlib.reload(test_module) - - mock_presentation_exchange.retrieve_by_id = mock.CoroutineMock( - return_value=mock.MagicMock( - state=mock_presentation_exchange.STATE_VERIFIED, - connection_id="dummy", - delete_record=mock.CoroutineMock( - side_effect=test_module.StorageError() - ), - ) - ) - - with self.assertRaises(test_module.web.HTTPBadRequest): - await test_module.presentation_exchange_remove(self.request) - - async def test_register(self): - mock_app = mock.MagicMock() - mock_app.add_routes = mock.MagicMock() - - await test_module.register(mock_app) - mock_app.add_routes.assert_called_once() - - async def test_post_process_routes(self): - mock_app = mock.MagicMock(_state={"swagger_dict": {}}) - test_module.post_process_routes(mock_app) - assert "tags" in mock_app._state["swagger_dict"] diff --git a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_request.py b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_request.py index d9a6e525eb..49a6cc93be 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_request.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_request.py @@ -57,7 +57,7 @@ def __init__( list(request_presentations_attach) if request_presentations_attach else [] ) - def attachment(self, fmt: V20PresFormat.Format = None) -> dict: + def attachment(self, fmt: V20PresFormat.Format | None = None) -> dict | None: """Return attached presentation request item. Args: diff --git a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py index f0ccd46859..de67b3c177 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/models/pres_exchange.py @@ -242,6 +242,14 @@ def __eq__(self, other: Any) -> bool: """Comparison between records.""" return super().__eq__(other) + def get_indy_proof_request(self): + """Retrieve Indy Proof request from record.""" + proof_request = self.pres_request.attachment(V20PresFormat.Format.INDY) + if not proof_request: + raise ValueError("No indy proof request on this record") + + return proof_request + class V20PresExRecordSchema(BaseExchangeSchema): """Schema for de/serialization of v2.0 presentation exchange records.""" diff --git a/aries_cloudagent/revocation/manager.py b/aries_cloudagent/revocation/manager.py index ed0312dd92..4ab26be4fa 100644 --- a/aries_cloudagent/revocation/manager.py +++ b/aries_cloudagent/revocation/manager.py @@ -8,10 +8,6 @@ from ..core.error import BaseError from ..core.profile import Profile from ..indy.issuer import IndyIssuer -from ..protocols.issue_credential.v1_0.models.credential_exchange import ( - V10CredentialExchange, -) -from ..protocols.issue_credential.v2_0.models.cred_ex_record import V20CredExRecord from ..protocols.revocation_notification.v1_0.models.rev_notification_record import ( RevNotificationRecord, ) @@ -410,49 +406,13 @@ async def set_cred_revoked_state( """ for cred_rev_id in cred_rev_ids: - cred_ex_id = None - try: async with self._profile.transaction() as txn: rev_rec = await IssuerCredRevRecord.retrieve_by_ids( txn, rev_reg_id, cred_rev_id, for_update=True ) - cred_ex_id = rev_rec.cred_ex_id - cred_ex_version = rev_rec.cred_ex_version rev_rec.state = IssuerCredRevRecord.STATE_REVOKED await rev_rec.save(txn, reason="revoke credential") await txn.commit() except StorageNotFoundError: continue - - async with self._profile.transaction() as txn: - if ( - not cred_ex_version - or cred_ex_version == IssuerCredRevRecord.VERSION_1 - ): - try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - txn, cred_ex_id, for_update=True - ) - cred_ex_record.state = ( - V10CredentialExchange.STATE_CREDENTIAL_REVOKED - ) - await cred_ex_record.save(txn, reason="revoke credential") - await txn.commit() - continue # skip 2.0 record check - except StorageNotFoundError: - pass - - if ( - not cred_ex_version - or cred_ex_version == IssuerCredRevRecord.VERSION_2 - ): - try: - cred_ex_record = await V20CredExRecord.retrieve_by_id( - txn, cred_ex_id, for_update=True - ) - cred_ex_record.state = V20CredExRecord.STATE_CREDENTIAL_REVOKED - await cred_ex_record.save(txn, reason="revoke credential") - await txn.commit() - except StorageNotFoundError: - pass diff --git a/aries_cloudagent/revocation_anoncreds/manager.py b/aries_cloudagent/revocation_anoncreds/manager.py index edbdb1ab78..e614e6cda5 100644 --- a/aries_cloudagent/revocation_anoncreds/manager.py +++ b/aries_cloudagent/revocation_anoncreds/manager.py @@ -7,10 +7,6 @@ from ..anoncreds.revocation import AnonCredsRevocation from ..core.error import BaseError from ..core.profile import Profile -from ..protocols.issue_credential.v1_0.models.credential_exchange import ( - V10CredentialExchange, -) -from ..protocols.issue_credential.v2_0.models.cred_ex_record import V20CredExRecord from ..protocols.revocation_notification.v1_0.models.rev_notification_record import ( RevNotificationRecord, ) @@ -322,7 +318,7 @@ async def clear_pending_revocations( return result async def set_cred_revoked_state( - self, rev_reg_id: str, cred_rev_ids: Sequence[int] + self, rev_reg_id: str, cred_rev_ids: Sequence[str] ) -> None: """Update credentials state to credential_revoked. @@ -335,49 +331,13 @@ async def set_cred_revoked_state( """ for cred_rev_id in cred_rev_ids: - cred_ex_id = None - try: async with self._profile.transaction() as txn: rev_rec = await IssuerCredRevRecord.retrieve_by_ids( - txn, rev_reg_id, str(cred_rev_id), for_update=True + txn, rev_reg_id, cred_rev_id, for_update=True ) - cred_ex_id = rev_rec.cred_ex_id - cred_ex_version = rev_rec.cred_ex_version rev_rec.state = IssuerCredRevRecord.STATE_REVOKED await rev_rec.save(txn, reason="revoke credential") await txn.commit() except StorageNotFoundError: continue - - async with self._profile.transaction() as txn: - if ( - not cred_ex_version - or cred_ex_version == IssuerCredRevRecord.VERSION_1 - ): - try: - cred_ex_record = await V10CredentialExchange.retrieve_by_id( - txn, cred_ex_id, for_update=True - ) - cred_ex_record.state = ( - V10CredentialExchange.STATE_CREDENTIAL_REVOKED - ) - await cred_ex_record.save(txn, reason="revoke credential") - await txn.commit() - continue # skip 2.0 record check - except StorageNotFoundError: - pass - - if ( - not cred_ex_version - or cred_ex_version == IssuerCredRevRecord.VERSION_2 - ): - try: - cred_ex_record = await V20CredExRecord.retrieve_by_id( - txn, cred_ex_id, for_update=True - ) - cred_ex_record.state = V20CredExRecord.STATE_CREDENTIAL_REVOKED - await cred_ex_record.save(txn, reason="revoke credential") - await txn.commit() - except StorageNotFoundError: - pass