Skip to content

Commit

Permalink
Merge pull request openwallet-foundation#2394 from dbluhm/feature/did…
Browse files Browse the repository at this point in the history
…x-reject

feat: add DID Exchange specific problem reports and reject endpoint
  • Loading branch information
dbluhm authored Aug 9, 2023
2 parents db2d9d4 + c5d8c17 commit ffe4502
Show file tree
Hide file tree
Showing 19 changed files with 517 additions and 66 deletions.
7 changes: 7 additions & 0 deletions aries_cloudagent/connections/models/conn_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,13 @@ async def delete_record(self, session: ProfileSession):
{"connection_id": self.connection_id},
)

async def abandon(self, session: ProfileSession, *, reason: Optional[str] = None):
"""Set state to abandoned."""
reason = reason or "Connectin abandoned"
self.state = ConnRecord.State.ABANDONED.rfc23
self.error_msg = reason
await self.save(session, reason=reason)

async def metadata_get(
self, session: ProfileSession, key: str, default: Any = None
) -> Any:
Expand Down
9 changes: 9 additions & 0 deletions aries_cloudagent/messaging/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@ def Schema(self) -> Type["BaseModelSchema"]:
"""
return self._get_schema_class()

@overload
@classmethod
def deserialize(
cls: Type[ModelType],
obj,
) -> ModelType:
"""Convert from JSON representation to a model instance."""
...

@overload
@classmethod
def deserialize(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
)

from ....out_of_band.v1_0.messages.invitation import InvitationMessage
from ....problem_report.v1_0.message import ProblemReport

from ..messages.problem_report_reason import ProblemReportReason
from ..messages.problem_report import DIDXProblemReport, ProblemReportReason


class InvitationHandler(BaseHandler):
Expand All @@ -27,7 +26,7 @@ async def handle(self, context: RequestContext, responder: BaseResponder):
self._logger.debug(f"InvitationHandler called with context {context}")
assert isinstance(context.message, InvitationMessage)

report = ProblemReport(
report = DIDXProblemReport(
description={
"code": ProblemReportReason.INVITATION_NOT_ACCEPTED.value,
"en": (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Problem report handler for DID Exchange."""

from .....messaging.base_handler import (
BaseHandler,
BaseResponder,
HandlerException,
RequestContext,
)
from ..manager import DIDXManager, DIDXManagerError
from ..messages.problem_report import DIDXProblemReport


class DIDXProblemReportHandler(BaseHandler):
"""Handler class for DID Exchange problem report messages."""

async def handle(self, context: RequestContext, responder: BaseResponder):
"""Handle problem report message."""
self._logger.debug(f"DIDXProblemReportHandler called with context {context}")
assert isinstance(context.message, DIDXProblemReport)

self._logger.info("Received problem report: %s", context.message.description)
profile = context.profile
mgr = DIDXManager(profile)
try:
if context.connection_record:
await mgr.receive_problem_report(
context.connection_record, context.message
)
else:
raise HandlerException("No connection established for problem report")
except DIDXManagerError:
# Unrecognized problem report code
self._logger.exception("Error receiving DID Exchange problem report")
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""Connection request handler under RFC 23 (DID exchange)."""

from aries_cloudagent.protocols.didexchange.v1_0.messages.problem_report import (
DIDXProblemReport,
)
from .....connections.models.conn_record import ConnRecord
from .....messaging.base_handler import BaseHandler, BaseResponder, RequestContext
from ....coordinate_mediation.v1_0.manager import MediationManager
from ....problem_report.v1_0.message import ProblemReport
from ..manager import DIDXManager, DIDXManagerError
from ..messages.request import DIDXRequest

Expand Down Expand Up @@ -75,6 +77,8 @@ async def handle(self, context: RequestContext, responder: BaseResponder):
"Error parsing DIDDoc for problem report"
)
await responder.send_reply(
ProblemReport(description={"en": e.message, "code": e.error_code}),
DIDXProblemReport(
description={"en": e.message, "code": e.error_code}
),
target_list=targets,
)
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""DID exchange response handler under RFC 23."""

from aries_cloudagent.protocols.didexchange.v1_0.messages.problem_report import (
DIDXProblemReport,
)
from .....messaging.base_handler import (
BaseHandler,
BaseResponder,
RequestContext,
)

from ....problem_report.v1_0.message import ProblemReport
from ....trustping.v1_0.messages.ping import Ping

from ..manager import DIDXManager, DIDXManagerError
Expand Down Expand Up @@ -48,7 +50,9 @@ async def handle(self, context: RequestContext, responder: BaseResponder):
"Error parsing DIDDoc for problem report"
)
await responder.send_reply(
ProblemReport(description={"en": e.message, "code": e.error_code}),
DIDXProblemReport(
description={"en": e.message, "code": e.error_code}
),
target_list=targets,
)
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from ...manager import DIDXManagerError
from ...messages.complete import DIDXComplete
from ...messages.problem_report_reason import ProblemReportReason
from ...messages.problem_report import ProblemReportReason

from .. import complete_handler as test_module
from ......wallet.did_method import DIDMethods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
from ......transport.inbound.receipt import MessageReceipt

from .....out_of_band.v1_0.messages.invitation import InvitationMessage
from .....problem_report.v1_0.message import ProblemReport

from ...handlers.invitation_handler import InvitationHandler
from ...messages.problem_report_reason import ProblemReportReason
from ...messages.problem_report import DIDXProblemReport, ProblemReportReason
from ......wallet.did_method import DIDMethods


Expand All @@ -30,8 +29,12 @@ async def test_problem_report(self, request_context):
messages = responder.messages
assert len(messages) == 1
result, target = messages[0]
assert isinstance(result, ProblemReport) and (
result.description["code"]
== ProblemReportReason.INVITATION_NOT_ACCEPTED.value
assert (
isinstance(result, DIDXProblemReport)
and result.description
and (
result.description["code"]
== ProblemReportReason.INVITATION_NOT_ACCEPTED.value
)
)
assert not target
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from asynctest import mock as async_mock
import pytest

from .. import problem_report_handler as test_module
from ......messaging.base_handler import HandlerException
from ......messaging.request_context import RequestContext
from ......messaging.responder import MockResponder
from ...manager import DIDXManagerError
from ...messages.problem_report import DIDXProblemReport


@pytest.fixture()
def request_context():
ctx = RequestContext.test_context()
yield ctx


class TestDIDXProblemReportHandler:
"""Unit test problem report handler."""

@pytest.mark.asyncio
@async_mock.patch.object(test_module, "DIDXManager")
async def test_called(self, manager, request_context):
manager.return_value.receive_problem_report = async_mock.CoroutineMock()
request_context.message = DIDXProblemReport()
request_context.connection_record = async_mock.MagicMock()
handler_inst = test_module.DIDXProblemReportHandler()
responder = MockResponder()
await handler_inst.handle(request_context, responder)
assert not responder.messages
assert manager.return_value.receive_problem_report.called_once()

@pytest.mark.asyncio
@async_mock.patch.object(test_module, "DIDXManager")
async def test_called_no_conn(self, manager, request_context):
manager.return_value.receive_problem_report = async_mock.CoroutineMock()
request_context.message = DIDXProblemReport()
handler_inst = test_module.DIDXProblemReportHandler()
responder = MockResponder()
with pytest.raises(HandlerException):
await handler_inst.handle(request_context, responder)

@pytest.mark.asyncio
@async_mock.patch.object(test_module, "DIDXManager")
async def test_called_unrecognized_report_exception(
self, manager, request_context, caplog
):
manager.return_value.receive_problem_report = async_mock.CoroutineMock(
side_effect=DIDXManagerError()
)
request_context.message = DIDXProblemReport()
request_context.connection_record = async_mock.MagicMock()
handler_inst = test_module.DIDXProblemReportHandler()
responder = MockResponder()
await handler_inst.handle(request_context, responder)
assert "Error receiving DID Exchange problem report" in caplog.text
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
from asynctest import mock as async_mock
from asynctest import TestCase as AsyncTestCase

from ......connections.models import connection_target, conn_record
from ......connections.models.diddoc import (
DIDDoc,
PublicKey,
PublicKeyType,
Service,
)
from ......connections.models import conn_record, connection_target
from ......connections.models.diddoc import DIDDoc, PublicKey, PublicKeyType, Service
from ......core.in_memory import InMemoryProfile
from ......wallet.did_method import SOV, DIDMethods
from ......wallet.key_type import ED25519
from ......messaging.decorators.attach_decorator import AttachDecorator
from ......messaging.request_context import RequestContext
from ......messaging.responder import MockResponder
from ......transport.inbound.receipt import MessageReceipt

from .....problem_report.v1_0.message import ProblemReport

from ......wallet.did_method import DIDMethods, SOV
from ......wallet.key_type import ED25519
from ...handlers import request_handler as test_module
from ...manager import DIDXManagerError
from ...messages.problem_report import DIDXProblemReport, ProblemReportReason
from ...messages.request import DIDXRequest
from ...messages.problem_report_reason import ProblemReportReason

TEST_DID = "55GkHamhTU1ZbTbV2ab9DE"
TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx"
Expand Down Expand Up @@ -184,8 +176,13 @@ async def test_problem_report(self, mock_didx_mgr):
messages = responder.messages
assert len(messages) == 1
result, target = messages[0]
assert isinstance(result, ProblemReport) and (
result.description["code"] == ProblemReportReason.REQUEST_NOT_ACCEPTED.value
assert (
isinstance(result, DIDXProblemReport)
and result.description
and (
result.description["code"]
== ProblemReportReason.REQUEST_NOT_ACCEPTED.value
)
)
assert target == {"target_list": None}

Expand All @@ -211,8 +208,13 @@ async def test_problem_report_did_doc(self, mock_conn_target, mock_didx_mgr):
messages = responder.messages
assert len(messages) == 1
result, target = messages[0]
assert isinstance(result, ProblemReport) and (
result.description["code"] == ProblemReportReason.REQUEST_NOT_ACCEPTED.value
assert (
isinstance(result, DIDXProblemReport)
and result.description
and (
result.description["code"]
== ProblemReportReason.REQUEST_NOT_ACCEPTED.value
)
)
assert target == {"target_list": [mock_conn_target]}

Expand Down Expand Up @@ -242,7 +244,12 @@ async def test_problem_report_did_doc_no_conn_target(
messages = responder.messages
assert len(messages) == 1
result, target = messages[0]
assert isinstance(result, ProblemReport) and (
result.description["code"] == ProblemReportReason.REQUEST_NOT_ACCEPTED.value
assert (
isinstance(result, DIDXProblemReport)
and result.description
and (
result.description["code"]
== ProblemReportReason.REQUEST_NOT_ACCEPTED.value
)
)
assert target == {"target_list": None}
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@
from ......wallet.did_method import SOV, DIDMethods
from ......wallet.key_type import ED25519

from .....problem_report.v1_0.message import ProblemReport
from .....trustping.v1_0.messages.ping import Ping

from ...handlers import response_handler as test_module
from ...manager import DIDXManagerError
from ...messages.response import DIDXResponse
from ...messages.problem_report_reason import ProblemReportReason
from ...messages.problem_report import DIDXProblemReport, ProblemReportReason

TEST_DID = "55GkHamhTU1ZbTbV2ab9DE"
TEST_VERKEY = "3Dn1SJNPaCXcvvJvSbsFWP2xaCjMom3can8CQNhWrTRx"
Expand Down Expand Up @@ -125,9 +124,13 @@ async def test_problem_report(self, mock_didx_mgr):
messages = responder.messages
assert len(messages) == 1
result, target = messages[0]
assert isinstance(result, ProblemReport) and (
result.description["code"]
== ProblemReportReason.RESPONSE_NOT_ACCEPTED.value
assert (
isinstance(result, DIDXProblemReport)
and result.description
and (
result.description["code"]
== ProblemReportReason.RESPONSE_NOT_ACCEPTED.value
)
)
assert target == {"target_list": None}

Expand Down Expand Up @@ -157,9 +160,13 @@ async def test_problem_report_did_doc(
messages = responder.messages
assert len(messages) == 1
result, target = messages[0]
assert isinstance(result, ProblemReport) and (
result.description["code"]
== ProblemReportReason.RESPONSE_NOT_ACCEPTED.value
assert (
isinstance(result, DIDXProblemReport)
and result.description
and (
result.description["code"]
== ProblemReportReason.RESPONSE_NOT_ACCEPTED.value
)
)
assert target == {"target_list": [mock_conn_target]}

Expand Down Expand Up @@ -189,8 +196,12 @@ async def test_problem_report_did_doc_no_conn_target(
messages = responder.messages
assert len(messages) == 1
result, target = messages[0]
assert isinstance(result, ProblemReport) and (
result.description["code"]
== ProblemReportReason.RESPONSE_NOT_ACCEPTED.value
assert (
isinstance(result, DIDXProblemReport)
and result.description
and (
result.description["code"]
== ProblemReportReason.RESPONSE_NOT_ACCEPTED.value
)
)
assert target == {"target_list": None}
Loading

0 comments on commit ffe4502

Please sign in to comment.