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

Did posture include ledger non public #691

2 changes: 0 additions & 2 deletions aries_cloudagent/admin/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ def topic_filter(self, val: Sequence[str]):
async def ready_middleware(request: web.BaseRequest, handler: Coroutine):
"""Only continue if application is ready to take work."""

print(f'\n\n== ready 1 {request.rel_url}, ready={request.app._state.get("ready")}')
if (
str(request.rel_url).rstrip("/")
in (
Expand Down Expand Up @@ -162,7 +161,6 @@ async def ready_middleware(request: web.BaseRequest, handler: Coroutine):
LOGGER.error("Handler error with exception: %s", str(e))
raise

print(".. ready N")
raise web.HTTPServiceUnavailable(reason="Shutdown in progress")


Expand Down
5 changes: 5 additions & 0 deletions aries_cloudagent/ledger/indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from ..storage.indy import IndyStorage
from ..utils import sentinel
from ..wallet.base import BaseWallet, DIDInfo
from ..wallet.did_posture import DIDPosture

from .base import BaseLedger
from .endpoint_type import EndpointType
Expand Down Expand Up @@ -877,6 +878,10 @@ async def register_nym(

await self._submit(request_json)

did_info = await self.wallet.get_local_did(did)
metadata = {**did_info.metadata, **DIDPosture.POSTED.metadata}
await self.wallet.replace_local_did_metadata(did, metadata)

async def get_nym_role(self, did: str) -> Role:
"""
Return the role of the input public DID's NYM on the ledger.
Expand Down
7 changes: 7 additions & 0 deletions aries_cloudagent/ledger/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ async def register_ledger_nym(request: web.BaseRequest):
raise web.HTTPForbidden(reason=err.roll_up)
except LedgerError as err:
raise web.HTTPBadRequest(reason=err.roll_up)
except WalletError as err:
raise web.HTTPBadRequest(
reason=(
f"Registered NYM for DID {did} on ledger but could not "
f"replace metadata in wallet: {err.roll_up}"
)
)

return web.json_response({"success": success})

Expand Down
6 changes: 3 additions & 3 deletions aries_cloudagent/ledger/tests/test_endpoint_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

class TestEndpointType(AsyncTestCase):
async def test_endpoint_type(self):
assert EndpointType.ENDPOINT == EndpointType.get("endpoint")
assert EndpointType.PROFILE == EndpointType.get("PROFILE")
assert EndpointType.LINKED_DOMAINS == EndpointType.get("linked_domains")
assert EndpointType.ENDPOINT is EndpointType.get("endpoint")
assert EndpointType.PROFILE is EndpointType.get("PROFILE")
assert EndpointType.LINKED_DOMAINS is EndpointType.get("linked_domains")
assert EndpointType.get("no-such-type") is None
assert EndpointType.get(None) is None

Expand Down
7 changes: 5 additions & 2 deletions aries_cloudagent/ledger/tests/test_indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2168,8 +2168,11 @@ async def test_update_endpoint_for_did_read_only(
async def test_register_nym(
self, mock_submit, mock_build_nym_req, mock_close, mock_open
):
mock_wallet = async_mock.MagicMock()
mock_wallet.type = "indy"
mock_wallet = async_mock.MagicMock(
type="indy",
get_local_did=async_mock.CoroutineMock(),
replace_local_did_metadata=async_mock.CoroutineMock(),
)

ledger = IndyLedger("name", mock_wallet)

Expand Down
8 changes: 8 additions & 0 deletions aries_cloudagent/ledger/tests/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,14 @@ async def test_register_nym_ledger_error(self):
with self.assertRaises(test_module.web.HTTPBadRequest):
await test_module.register_ledger_nym(request)

async def test_register_nym_wallet_error(self):
request = async_mock.MagicMock()
request.app = self.app
request.query = {"did": self.test_did, "verkey": self.test_verkey}
self.ledger.register_nym.side_effect = test_module.WalletError("Error")
with self.assertRaises(test_module.web.HTTPBadRequest):
await test_module.register_ledger_nym(request)

async def test_get_nym_role(self):
request = async_mock.MagicMock()
request.app = self.app
Expand Down
17 changes: 17 additions & 0 deletions aries_cloudagent/messaging/tests/test_valid.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
BASE64URL,
BASE64URL_NO_PAD,
DID_KEY,
DID_POSTURE,
ENDPOINT,
ENDPOINT_TYPE,
INDY_CRED_DEF_ID,
Expand Down Expand Up @@ -145,6 +146,22 @@ def test_did_key(self):

DID_KEY["validate"]("did:key:zQ4zqM7aXqm7gDQkUVLng9h")

def test_did_posture(self):
non_did_postures = [
"not-me",
None,
"PUBLIC",
"Posted",
"wallet only",
]
for non_did_posture in non_did_postures:
with self.assertRaises(ValidationError):
DID_POSTURE["validate"](non_did_posture)

DID_POSTURE["validate"]("public")
DID_POSTURE["validate"]("posted")
DID_POSTURE["validate"]("wallet_only")

def test_indy_base58_sha256_hash(self):
non_base58_sha256_hashes = [
"Q4zqM7aXqm7gDQkUVLng9JQ4zqM7aXqm7gDQkUVLng9I", # 'I' not a base58 char
Expand Down
16 changes: 16 additions & 0 deletions aries_cloudagent/messaging/valid.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .util import epoch_to_str

from ..ledger.endpoint_type import EndpointType as EndpointTypeEnum
from ..wallet.did_posture import DIDPosture as DIDPostureEnum

B58 = alphabet if isinstance(alphabet, str) else alphabet.decode("ascii")

Expand Down Expand Up @@ -114,6 +115,20 @@ def __init__(self):
)


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

EXAMPLE = DIDPostureEnum.WALLET_ONLY.moniker

def __init__(self):
"""Initializer."""

super().__init__(
choices=[did_posture.moniker for did_posture in DIDPostureEnum],
error="Value {input} must be one of {choices}",
)


class IndyDID(Regexp):
"""Validate value against indy DID."""

Expand Down Expand Up @@ -455,6 +470,7 @@ def __init__(self):
JWS_HEADER_KID = {"validate": JWSHeaderKid(), "example": JWSHeaderKid.EXAMPLE}
JWT = {"validate": JSONWebToken(), "example": JSONWebToken.EXAMPLE}
DID_KEY = {"validate": DIDKey(), "example": DIDKey.EXAMPLE}
DID_POSTURE = {"validate": DIDPosture(), "example": DIDPosture.EXAMPLE}
INDY_DID = {"validate": IndyDID(), "example": IndyDID.EXAMPLE}
INDY_RAW_PUBLIC_KEY = {
"validate": IndyRawPublicKey(),
Expand Down
43 changes: 29 additions & 14 deletions aries_cloudagent/wallet/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from ..ledger.base import BaseLedger
from ..ledger.endpoint_type import EndpointType

from .did_posture import DIDPosture

KeyInfo = namedtuple("KeyInfo", "verkey metadata")
DIDInfo = namedtuple("DIDInfo", "did verkey metadata")

Expand Down Expand Up @@ -141,7 +143,7 @@ async def create_local_did(
Create and store a new local DID.

Args:
seed: Optional seed to use for did
seed: Optional seed to use for DID
did: The DID to use
metadata: Metadata to store with DID

Expand All @@ -159,7 +161,7 @@ async def create_public_did(
Implicitly flags all other dids as not public.

Args:
seed: Optional seed to use for did
seed: Optional seed to use for DID
did: The DID to use
metadata: Metadata to store with DID

Expand All @@ -168,7 +170,7 @@ async def create_public_did(

"""

metadata["public"] = True
metadata = DIDPosture.PUBLIC.metadata
dids = await self.get_local_dids()
for info in dids:
info_meta = info.metadata
Expand All @@ -178,7 +180,7 @@ async def create_public_did(

async def get_public_did(self) -> DIDInfo:
"""
Retrieve the public did.
Retrieve the public DID.

Returns:
The created `DIDInfo`
Expand All @@ -187,14 +189,14 @@ async def get_public_did(self) -> DIDInfo:

dids = await self.get_local_dids()
for info in dids:
if "public" in info.metadata and info.metadata["public"] is True:
if info.metadata.get("public"):
return info

return None

async def set_public_did(self, did: str) -> DIDInfo:
"""
Assign the public did.
Assign the public DID.

Returns:
The created `DIDInfo`
Expand All @@ -214,8 +216,7 @@ async def set_public_did(self, did: str) -> DIDInfo:
await self.replace_local_did_metadata(public.did, metadata)

if info:
metadata = info.metadata.copy()
metadata["public"] = True
metadata = {**info.metadata, **DIDPosture.PUBLIC.metadata}
await self.replace_local_did_metadata(info.did, metadata)
info = await self.get_local_did(info.did)

Expand All @@ -237,7 +238,7 @@ async def get_local_did(self, did: str) -> DIDInfo:
Find info for a local DID.

Args:
did: The DID to get info for
did: The DID for which to get info

Returns:
A `DIDInfo` instance for the DID
Expand All @@ -250,7 +251,7 @@ async def get_local_did_for_verkey(self, verkey: str) -> DIDInfo:
Resolve a local DID from a verkey.

Args:
verkey: Verkey to get DID info for
verkey: Verkey for which to get DID info

Returns:
A `DIDInfo` instance for the DID
Expand All @@ -270,6 +271,20 @@ async def replace_local_did_metadata(self, did: str, metadata: dict):

"""

async def get_posted_dids(self) -> Sequence[DIDInfo]:
"""
Get list of defined posted DIDs, excluding public DID.

Returns:
A list of `DIDInfo` instances

"""
return [
info
for info in await self.get_local_dids()
if info.metadata.get("posted") and not info.metadata.get("public")
]

async def set_did_endpoint(
self,
did: str,
Expand All @@ -278,12 +293,13 @@ async def set_did_endpoint(
endpoint_type: EndpointType = None,
):
"""
Update the endpoint for a DID in the wallet, send to ledger if public.
Update the endpoint for a DID in the wallet, send to ledger if public or posted.

Args:
did: DID for which to set endpoint
endpoint: the endpoint to set, None to clear
ledger: the ledger to which to send endpoint update if DID is public
ledger: the ledger to which to send endpoint update if
DID is public or posted
endpoint_type: the type of the endpoint/service. Only endpoint_type
'endpoint' affects local wallet
"""
Expand All @@ -292,8 +308,7 @@ async def set_did_endpoint(
if not endpoint_type:
endpoint_type = EndpointType.ENDPOINT
if endpoint_type == EndpointType.ENDPOINT:
metadata.pop("endpoint", None)
metadata["endpoint"] = endpoint
metadata[endpoint_type.indy] = endpoint

await self.replace_local_did_metadata(did, metadata)

Expand Down
12 changes: 6 additions & 6 deletions aries_cloudagent/wallet/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ async def create_local_did(
Create and store a new local DID.

Args:
seed: Optional seed to use for did
seed: Optional seed to use for DID
did: The DID to use
metadata: Metadata to store with DID

Expand Down Expand Up @@ -252,7 +252,7 @@ async def get_local_did(self, did: str) -> DIDInfo:
Find info for a local DID.

Args:
did: The DID to get info for
did: The DID for which to get info

Returns:
A `DIDInfo` instance representing the found DID
Expand All @@ -270,7 +270,7 @@ async def get_local_did_for_verkey(self, verkey: str) -> DIDInfo:
Resolve a local DID from a verkey.

Args:
verkey: The verkey to get the local DID for
verkey: The verkey for which to get the local DID

Returns:
A `DIDInfo` instance representing the found DID
Expand All @@ -289,7 +289,7 @@ async def replace_local_did_metadata(self, did: str, metadata: dict):
Replace metadata for a local DID.

Args:
did: The DID to replace metadata for
did: The DID for which to replace metadata
metadata: The new metadata

Raises:
Expand Down Expand Up @@ -384,8 +384,8 @@ async def pack_message(

Args:
message: The message to pack
to_verkeys: List of verkeys to pack for
from_verkey: Sender verkey to pack from
to_verkeys: List of verkeys for which to pack
from_verkey: Sender verkey from which to pack

Returns:
The resulting packed message bytes
Expand Down
50 changes: 50 additions & 0 deletions aries_cloudagent/wallet/did_posture.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""Ledger utilities."""

from collections import namedtuple
from enum import Enum
from typing import Mapping, Union

DIDPostureSpec = namedtuple("DIDPostureTuple", "moniker ordinal public posted")


class DIDPosture(Enum):
"""Enum for DID postures: public, posted but not public, or in wallet only."""

PUBLIC = DIDPostureSpec("public", 0, True, True)
POSTED = DIDPostureSpec("posted", 1, False, True)
WALLET_ONLY = DIDPostureSpec("wallet_only", 2, False, False)

@staticmethod
def get(posture: Union[str, Mapping]) -> "DIDPosture":
"""Return enum instance corresponding to input string or DID metadata."""
if posture is None:
return None

elif isinstance(posture, str):
for did_posture in DIDPosture:
if posture.lower() == did_posture.value.moniker:
return did_posture

elif posture.get("public"):
return DIDPosture.PUBLIC
elif posture.get("posted"):
return DIDPosture.POSTED
elif isinstance(posture, Mapping):
return DIDPosture.WALLET_ONLY

return None

@property
def moniker(self) -> str:
"""Name for DID posture."""
return self.value.moniker

@property
def metadata(self) -> Mapping:
"""DID metadata for DID posture."""
return {"public": self.value.public, "posted": self.value.posted}

@property
def ordinal(self) -> Mapping:
"""Ordinal for presentation: public first, then posted and wallet-only."""
return self.value.ordinal
Loading