Skip to content

Commit

Permalink
did:tdw resolver (#3237)
Browse files Browse the repository at this point in the history
* did:tdw resolver

Signed-off-by: jamshale <[email protected]>

* Update unit tests

Signed-off-by: jamshale <[email protected]>

* Update poetry.lock

Signed-off-by: jamshale <[email protected]>

---------

Signed-off-by: jamshale <[email protected]>
  • Loading branch information
jamshale authored Nov 18, 2024
1 parent c18f6d1 commit f5c49b0
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 5 deletions.
19 changes: 19 additions & 0 deletions acapy_agent/messaging/tests/test_valid.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
CREDENTIAL_TYPE_VALIDATE,
DID_KEY_VALIDATE,
DID_POSTURE_VALIDATE,
DID_TDW_VALIDATE,
ENDPOINT_TYPE_VALIDATE,
ENDPOINT_VALIDATE,
INDY_CRED_DEF_ID_VALIDATE,
Expand Down Expand Up @@ -114,6 +115,24 @@ def test_indy_did(self):
INDY_DID_VALIDATE("Q4zqM7aXqm7gDQkUVLng9h")
INDY_DID_VALIDATE("did:sov:Q4zqM7aXqm7gDQkUVLng9h")

def test_tdw_did(self):
valid_tdw_dids = [
"did:tdw:QmUchSB5f5DJQks9CeyLJjhAy4iKJcYzRyiuYq3sjV13px:example.com",
"did:tdw:QmZiKXwQVfyZVuvCsuHpQh4arSUpEmeVVRvSfv3uiEycSr:example.com%3A5000",
]
for valid_tdw_did in valid_tdw_dids:
DID_TDW_VALIDATE(valid_tdw_did)

non_valid_tdw_dids = [
"did:web:QmUchSB5f5DJQks9CeyLJjhAy4iKJcYzRyiuYq3sjV13px",
# Did urls are not allowed
"did:tdw:QmP9VWaTCHcyztDpRj9XSHvZbmYe3m9HZ61KoDtZgWaXVU:example.com%3A5000#z6MkkzY9skorPaoEbCJFKUo7thD8Yb8MBs28aJRopf1TUo9V",
"did:tdw:QmZiKXwQVfyZVuvCsuHpQh4arSUpEmeVVRvSfv3uiEycSr:example.com%3A5000/whois",
]
for non_valid_tdw_did in non_valid_tdw_dids:
with self.assertRaises(ValidationError):
DID_TDW_VALIDATE(non_valid_tdw_did)

def test_indy_raw_public_key(self):
non_indy_raw_public_keys = [
"Q4zqM7aXqm7gDQkUVLng9JQ4zqM7aXqm7gDQkUVLng9I", # 'I' not a base58 char
Expand Down
17 changes: 17 additions & 0 deletions acapy_agent/messaging/valid.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,20 @@ def __init__(self):
)


class DIDTdw(Regexp):
"""Validate value against did:tdw specification."""

EXAMPLE = "did:tdw:QmP9VWaTCHcyztDpRj9XSHvZbmYe3m9HZ61KoDtZgWaXVU:example.com%3A5000"
PATTERN = re.compile(r"^(did:tdw:)([a-zA-Z0-9%._-]*:)*[a-zA-Z0-9%._-]+$")

def __init__(self):
"""Initialize the instance."""

super().__init__(
DIDTdw.PATTERN, error="Value {input} is not in W3C did:tdw format"
)


class DIDPosture(OneOf):
"""Validate value against defined DID postures."""

Expand Down Expand Up @@ -934,6 +948,9 @@ def __init__(
DID_WEB_VALIDATE = DIDWeb()
DID_WEB_EXAMPLE = DIDWeb.EXAMPLE

DID_TDW_VALIDATE = DIDTdw()
DID_TDW_EXAMPLE = DIDTdw.EXAMPLE

ROUTING_KEY_VALIDATE = RoutingKey()
ROUTING_KEY_EXAMPLE = RoutingKey.EXAMPLE

Expand Down
6 changes: 6 additions & 0 deletions acapy_agent/resolver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ async def setup(context: InjectionContext):
await web_resolver.setup(context)
registry.register_resolver(web_resolver)

tdw_resolver = ClassProvider(
"acapy_agent.resolver.default.tdw.TdwDIDResolver"
).provide(context.settings, context.injector)
await tdw_resolver.setup(context)
registry.register_resolver(tdw_resolver)

if context.settings.get("resolver.universal"):
universal_resolver = ClassProvider(
"acapy_agent.resolver.default.universal.UniversalResolver"
Expand Down
40 changes: 40 additions & 0 deletions acapy_agent/resolver/default/tdw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""TDW DID Resolver.
Resolution is performed by the did_tdw library.
"""

from re import Pattern
from typing import Optional, Sequence, Text

from did_tdw.resolver import ResolutionResult, resolve_did

from ...config.injection_context import InjectionContext
from ...core.profile import Profile
from ...messaging.valid import DIDTdw
from ..base import BaseDIDResolver, ResolverType


class TdwDIDResolver(BaseDIDResolver):
"""TDW DID Resolver."""

def __init__(self):
"""Initialize the TDW DID Resolver."""
super().__init__(ResolverType.NATIVE)

async def setup(self, context: InjectionContext):
"""Perform required setup for TDW DID resolution."""

@property
def supported_did_regex(self) -> Pattern:
"""Return supported DID regex of TDW DID Resolver."""
return DIDTdw.PATTERN

async def _resolve(
self, profile: Profile, did: str, service_accept: Optional[Sequence[Text]] = None
) -> dict:
"""Resolve DID using TDW."""
response: ResolutionResult = await resolve_did(did)
if response.resolution_metadata and response.resolution_metadata.get("error"):
return response.resolution_metadata

return response.document
36 changes: 36 additions & 0 deletions acapy_agent/resolver/default/tests/test_tdw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import pytest

from ....core.profile import Profile
from ....messaging.valid import DIDTdw
from ....utils.testing import create_test_profile
from ..tdw import TdwDIDResolver

TEST_DID = "did:tdw:Qma6mc1qZw3NqxwX6SB5GPQYzP4pGN2nXD15Jwi4bcDBKu:domain.example"


@pytest.fixture
def resolver():
"""Resolver fixture."""
yield TdwDIDResolver()


@pytest.fixture
async def profile():
"""Profile fixture."""
yield await create_test_profile()


@pytest.mark.asyncio
async def test_supported_did_regex(profile, resolver: TdwDIDResolver):
"""Test the supported_did_regex."""
assert resolver.supported_did_regex == DIDTdw.PATTERN
assert await resolver.supports(
profile,
TEST_DID,
)


@pytest.mark.asyncio
async def test_resolve(resolver: TdwDIDResolver, profile: Profile):
"""Test resolve method."""
assert await resolver.resolve(profile, TEST_DID)
8 changes: 8 additions & 0 deletions acapy_agent/wallet/did_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ def holder_defined_did(self) -> HolderDefinedDid:
holder_defined_did=HolderDefinedDid.NO,
)

TDW = DIDMethod(
name="tdw",
key_types=[ED25519, X25519],
rotation=False,
holder_defined_did=HolderDefinedDid.NO,
)


class DIDMethods:
"""DID Method class specifying DID methods with supported key types."""
Expand All @@ -102,6 +109,7 @@ def __init__(self) -> None:
WEB.method_name: WEB,
PEER2.method_name: PEER2,
PEER4.method_name: PEER4,
TDW.method_name: TDW,
}

def registered(self, method: str) -> bool:
Expand Down
119 changes: 115 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,13 @@ requests = "~2.32.3"
rlp = "4.0.1"
unflatten = "~0.2"
sd-jwt = "^0.10.3"
uuid_utils = "^0.9.0"

# did libraries
did-peer-2 = "^0.1.2"
did-peer-4 = "^0.1.4"
uuid_utils = "^0.9.0"
did-tdw = "^0.2.1"


# askar
aries-askar = { version = "~0.3.2", optional = true }
Expand Down

0 comments on commit f5c49b0

Please sign in to comment.