Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/endpoint attrib structure #1934

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions aries_cloudagent/ledger/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,15 @@ async def _construct_attr_json(
if not routing_keys:
routing_keys = []

endpoint_dict = {"endpoint": endpoint}

if all_exist_endpoints:
all_exist_endpoints[endpoint_type.indy] = endpoint_dict
endpoint_dict["routingKeys"] = routing_keys
all_exist_endpoints[endpoint_type.indy] = endpoint
all_exist_endpoints["routingKeys"] = routing_keys
attr_json = json.dumps({"endpoint": all_exist_endpoints})

else:
endpoint_val = {endpoint_type.indy: endpoint_dict}
endpoint_dict = {endpoint_type.indy: endpoint}
endpoint_dict["routingKeys"] = routing_keys
attr_json = json.dumps({"endpoint": endpoint_val})
attr_json = json.dumps({"endpoint": endpoint_dict})

return attr_json

Expand Down
32 changes: 26 additions & 6 deletions aries_cloudagent/ledger/tests/test_indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2308,17 +2308,37 @@ async def test_construct_attr_json_with_routing_keys(self, mock_close, mock_open
attr_json = await ledger._construct_attr_json(
"https://url",
EndpointType.ENDPOINT,
all_exist_endpoints={"Endpoint": "https://endpoint"},
routing_keys=["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
)
assert attr_json == json.dumps(
{
"endpoint": {
"Endpoint": "https://endpoint",
"endpoint": {
"endpoint": "https://url",
"routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
},
"endpoint": "https://url",
"routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
}
}
)

@async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_open")
@async_mock.patch("aries_cloudagent.ledger.indy.IndySdkLedgerPool.context_close")
@pytest.mark.asyncio
async def test_construct_attr_json_with_routing_keys_all_exist_endpoints(
self, mock_close, mock_open
):
ledger = IndySdkLedger(IndySdkLedgerPool("name", checked=True), self.profile)
async with ledger:
attr_json = await ledger._construct_attr_json(
"https://url",
EndpointType.ENDPOINT,
all_exist_endpoints={"profile": "https://endpoint/profile"},
routing_keys=["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
)
assert attr_json == json.dumps(
{
"endpoint": {
"profile": "https://endpoint/profile",
"endpoint": "https://url",
"routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
}
}
)
Expand Down
40 changes: 20 additions & 20 deletions aries_cloudagent/ledger/tests/test_indy_vdr.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,27 +612,24 @@ async def test_update_endpoint_for_did(
"all_exist_endpoints, routing_keys, result",
[
(
{"Endpoint": "https://endpoint"},
{"profile": "https://endpoint/profile"},
["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
{
"endpoint": {
"Endpoint": "https://endpoint",
"endpoint": {
"endpoint": "https://url",
"routingKeys": [
"3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"
],
},
"profile": "https://endpoint/profile",
"endpoint": "https://url",
"routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
}
},
),
(
{"Endpoint": "https://endpoint"},
{"profile": "https://endpoint/profile"},
None,
{
"endpoint": {
"Endpoint": "https://endpoint",
"endpoint": {"endpoint": "https://url", "routingKeys": []},
"profile": "https://endpoint/profile",
"endpoint": "https://url",
"routingKeys": [],
}
},
),
Expand All @@ -641,21 +638,24 @@ async def test_update_endpoint_for_did(
["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
{
"endpoint": {
"endpoint": {
"endpoint": "https://url",
"routingKeys": [
"3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"
],
}
"endpoint": "https://url",
"routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
}
},
),
(None, None, {"endpoint": {"endpoint": "https://url", "routingKeys": []}}),
(
None,
None,
{
"profile": "https://endpoint/profile",
"spec_divergent_endpoint": "https://endpoint",
},
["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
{
"endpoint": {
"endpoint": {"endpoint": "https://url", "routingKeys": []}
"profile": "https://endpoint/profile",
"spec_divergent_endpoint": "https://endpoint",
"endpoint": "https://url",
"routingKeys": ["3YJCx3TqotDWFGv7JMR5erEvrmgu5y4FDqjR7sKWxgXn"],
}
},
),
Expand Down
147 changes: 80 additions & 67 deletions aries_cloudagent/resolver/default/indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
Resolution is performed using the IndyLedger class.
"""

from typing import Any, Mapping, Pattern
import logging
from typing import Optional, Pattern

from pydid import DID, DIDDocumentBuilder
from pydid.verification_method import Ed25519VerificationKey2018, VerificationMethod
Expand All @@ -21,6 +22,8 @@

from ..base import BaseDIDResolver, DIDNotFound, ResolverError, ResolverType

LOGGER = logging.getLogger(__name__)


class NoIndyLedger(ResolverError):
"""Raised when there is no Indy ledger instance configured."""
Expand All @@ -46,60 +49,85 @@ def supported_did_regex(self) -> Pattern:
"""Return supported_did_regex of Indy DID Resolver."""
return IndyDID.PATTERN

def _add_endpoint_as_endpoint_value_pair(
self,
builder: DIDDocumentBuilder,
endpoint: str,
recipient_key: VerificationMethod,
):
builder.service.add_didcomm(
ident=self.SERVICE_TYPE_DID_COMMUNICATION,
type_=self.SERVICE_TYPE_DID_COMMUNICATION,
service_endpoint=endpoint,
priority=1,
recipient_keys=[recipient_key],
routing_keys=[],
)

def _add_endpoint_as_map(
def process_endpoint_types(self, types):
"""Process endpoint types.

Returns expected types, subset of expected types,
or default types.
"""
expected_types = ["endpoint", "did-communication", "DIDComm"]
default_types = ["endpoint", "did-communication"]
if len(types) <= 0:
return default_types
for type in types:
if type not in expected_types:
return default_types
return types

def add_services(
self,
builder: DIDDocumentBuilder,
endpoint: Mapping[str, Any],
recipient_key: VerificationMethod,
endpoints: Optional[dict],
recipient_key: VerificationMethod = None,
):
types = endpoint.get("types", [self.SERVICE_TYPE_DID_COMMUNICATION])
routing_keys = endpoint.get("routingKeys", [])
endpoint_url = endpoint.get("endpoint")
if not endpoint_url:
raise ValueError("endpoint url not found in endpoint attrib")

if self.SERVICE_TYPE_DIDCOMM in types:
builder.service.add(
ident="#didcomm-1",
type_=self.SERVICE_TYPE_DIDCOMM,
service_endpoint=endpoint_url,
recipient_keys=[recipient_key.id],
routing_keys=routing_keys,
accept=["didcomm/v2"],
)
builder.context.append(self.CONTEXT_DIDCOMM_V2)
if self.SERVICE_TYPE_DID_COMMUNICATION in types:
builder.service.add(
ident="did-communication",
type_=self.SERVICE_TYPE_DID_COMMUNICATION,
service_endpoint=endpoint_url,
priority=1,
routing_keys=routing_keys,
recipient_keys=[recipient_key.id],
accept=["didcomm/aip2;env=rfc19"],
)
if self.SERVICE_TYPE_ENDPOINT in types:
builder.service.add(
ident="endpoint",
service_endpoint=endpoint_url,
type_=self.SERVICE_TYPE_ENDPOINT,
"""Add services."""
if not endpoints:
return

endpoint = endpoints.get("endpoint")
routing_keys = endpoints.get("routingKeys", [])
types = endpoints.get("types", [self.SERVICE_TYPE_DID_COMMUNICATION])

other_endpoints = {
key: endpoints[key]
for key in ("profile", "linked_domains")
if key in endpoints
}

if endpoint:
processed_types = self.process_endpoint_types(types)

if self.SERVICE_TYPE_ENDPOINT in processed_types:
builder.service.add(
ident="endpoint",
service_endpoint=endpoint,
type_=self.SERVICE_TYPE_ENDPOINT,
)

if self.SERVICE_TYPE_DID_COMMUNICATION in processed_types:
builder.service.add(
ident="did-communication",
type_=self.SERVICE_TYPE_DID_COMMUNICATION,
service_endpoint=endpoint,
priority=1,
routing_keys=routing_keys,
recipient_keys=[recipient_key.id],
accept=["didcomm/aip2;env=rfc19"],
)

if self.SERVICE_TYPE_DIDCOMM in types:
builder.service.add(
ident="#didcomm-1",
type_=self.SERVICE_TYPE_DIDCOMM,
service_endpoint=endpoint,
recipient_keys=[recipient_key.id],
routing_keys=routing_keys,
accept=["didcomm/v2"],
)
builder.context.append(self.CONTEXT_DIDCOMM_V2)
else:
LOGGER.warning(
"No endpoint for DID although endpoint attrib was resolvable"
)

if other_endpoints:
for type_, endpoint in other_endpoints.items():
builder.service.add(
ident=type_,
type_=EndpointType.get(type_).w3c,
service_endpoint=endpoint,
)

async def _resolve(self, profile: Profile, did: str) -> dict:
"""Resolve an indy DID."""
multitenant_mgr = profile.inject_or(BaseMultitenantManager)
Expand All @@ -119,7 +147,7 @@ async def _resolve(self, profile: Profile, did: str) -> dict:
try:
async with ledger:
recipient_key = await ledger.get_key_for_did(did)
endpoints = await ledger.get_all_endpoints_for_did(did)
endpoints: Optional[dict] = await ledger.get_all_endpoints_for_did(did)
except LedgerError as err:
raise DIDNotFound(f"DID {did} could not be resolved") from err

Expand All @@ -130,22 +158,7 @@ async def _resolve(self, profile: Profile, did: str) -> dict:
)
builder.authentication.reference(vmethod.id)
builder.assertion_method.reference(vmethod.id)
if endpoints:
for type_, endpoint in endpoints.items():
if type_ == EndpointType.ENDPOINT.indy:
if isinstance(endpoint, dict):
self._add_endpoint_as_map(builder, endpoint, vmethod)
else:
self._add_endpoint_as_endpoint_value_pair(
builder, endpoint, vmethod
)
else:
# Accept all service types for now, i.e. profile, linked_domains
builder.service.add(
ident=type_,
type_=type_,
service_endpoint=endpoint,
)
self.add_services(builder, endpoints, vmethod)

result = builder.build()
return result.serialize()
47 changes: 35 additions & 12 deletions aries_cloudagent/resolver/default/tests/test_indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest

from asynctest import mock as async_mock
from pydid.verification_method import VerificationMethod

from ....core.in_memory import InMemoryProfile
from ....core.profile import Profile
Expand Down Expand Up @@ -116,11 +117,11 @@ async def test_supports_updated_did_sov_rules(
):
"""Test that new attrib structure is supported."""
example = {
"endpoint": {
"endpoint": "https://example.com/endpoint",
"routingKeys": ["a-routing-key"],
"types": ["DIDComm", "did-communication", "endpoint"],
}
"endpoint": "https://example.com/endpoint",
"routingKeys": ["a-routing-key"],
"types": ["DIDComm", "did-communication", "endpoint"],
"profile": "https://example.com",
"linked_domains": "https://example.com",
}

ledger.get_all_endpoints_for_did = async_mock.CoroutineMock(
Expand All @@ -129,19 +130,41 @@ async def test_supports_updated_did_sov_rules(
assert await resolver.resolve(profile, TEST_DID0)

@pytest.mark.asyncio
async def test_supports_updated_did_sov_rules_x_no_endpoint_url(
async def test_supports_updated_did_sov_rules_no_endpoint_url(
self, resolver: IndyDIDResolver, ledger: BaseLedger, profile: Profile
):
"""Test that new attrib structure is supported."""
example = {
"endpoint": {
"routingKeys": ["a-routing-key"],
"types": ["DIDComm", "did-communication", "endpoint"],
}
"routingKeys": ["a-routing-key"],
"types": ["DIDComm", "did-communication", "endpoint"],
}

ledger.get_all_endpoints_for_did = async_mock.CoroutineMock(
return_value=example
)
with pytest.raises(ValueError):
await resolver.resolve(profile, TEST_DID0)
result = await resolver.resolve(profile, TEST_DID0)
assert "service" not in result

@pytest.mark.parametrize(
"types, result",
[
(
[],
["endpoint", "did-communication"],
),
(
["did-communication"],
["did-communication"],
),
(
["endpoint", "did-communication", "DIDComm", "other-endpoint-type"],
["endpoint", "did-communication"],
),
(
["endpoint", "did-communication", "DIDComm"],
["endpoint", "did-communication", "DIDComm"],
),
],
)
def test_process_endpoint_types(self, resolver: IndyDIDResolver, types, result):
assert resolver.process_endpoint_types(types) == result