Skip to content

Commit

Permalink
Merge branch 'main' into feature/create-all-registered-dids
Browse files Browse the repository at this point in the history
  • Loading branch information
chumbert authored Jan 16, 2023
2 parents c29d725 + 748ec9c commit 0c93947
Show file tree
Hide file tree
Showing 11 changed files with 374 additions and 158 deletions.
19 changes: 18 additions & 1 deletion aries_cloudagent/config/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1039,7 +1039,17 @@ def add_arguments(self, parser: ArgumentParser):
action="store_true",
env_var="ACAPY_PUBLIC_INVITES",
help=(
"Send invitations out, and receive connection requests, "
"Send invitations out using the public DID for the agent, "
"and receive connection requests solicited by invitations "
"which use the public DID. Default: false."
),
)
parser.add_argument(
"--requests-through-public-did",
action="store_true",
env_var="ACAPY_REQUESTS_THROUGH_PUBLIC_DID",
help=(
"Allow agent to receive unsolicited connection requests, "
"using the public DID for the agent. Default: false."
),
)
Expand Down Expand Up @@ -1134,6 +1144,13 @@ def get_settings(self, args: Namespace) -> dict:
settings["monitor_forward"] = args.monitor_forward
if args.public_invites:
settings["public_invites"] = True
if args.requests_through_public_did:
if not args.public_invites:
raise ArgsParseError(
"--public-invites is required to use "
"--requests-through-public-did"
)
settings["requests_through_public_did"] = True
if args.timing:
settings["timing.enabled"] = True
if args.timing_log:
Expand Down
18 changes: 3 additions & 15 deletions aries_cloudagent/connections/models/conn_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,11 +677,7 @@ class Meta:
required=False,
description="Routing state of connection",
validate=validate.OneOf(
[
getattr(ConnRecord, m)
for m in vars(ConnRecord)
if m.startswith("ROUTING_STATE_")
]
ConnRecord.get_attributes_by_prefix("ROUTING_STATE_", walk_mro=False)
),
example=ConnRecord.ROUTING_STATE_ACTIVE,
)
Expand All @@ -690,11 +686,7 @@ class Meta:
description="Connection acceptance: manual or auto",
example=ConnRecord.ACCEPT_AUTO,
validate=validate.OneOf(
[
getattr(ConnRecord, a)
for a in vars(ConnRecord)
if a.startswith("ACCEPT_")
]
ConnRecord.get_attributes_by_prefix("ACCEPT_", walk_mro=False)
),
)
error_msg = fields.Str(
Expand All @@ -707,11 +699,7 @@ class Meta:
description="Invitation mode",
example=ConnRecord.INVITATION_MODE_ONCE,
validate=validate.OneOf(
[
getattr(ConnRecord, i)
for i in vars(ConnRecord)
if i.startswith("INVITATION_MODE_")
]
ConnRecord.get_attributes_by_prefix("INVITATION_MODE_", walk_mro=False)
),
)
alias = fields.Str(
Expand Down
21 changes: 20 additions & 1 deletion aries_cloudagent/messaging/models/base_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class Meta:
EVENT_NAMESPACE: str = "acapy::record"
LOG_STATE_FLAG = None
TAG_NAMES = {"state"}
STATE_DELETED = "deleted"

def __init__(
self,
Expand Down Expand Up @@ -420,7 +421,7 @@ async def delete_record(self, session: ProfileSession):
storage = session.inject(BaseStorage)
if self.state:
self._previous_state = self.state
self.state = "deleted"
self.state = BaseRecord.STATE_DELETED
await self.emit_event(session, self.serialize())
await storage.delete_record(self.storage_record)

Expand Down Expand Up @@ -497,6 +498,24 @@ def __eq__(self, other: Any) -> bool:
return self.value == other.value and self.tags == other.tags
return False

@classmethod
def get_attributes_by_prefix(cls, prefix: str, walk_mro: bool = True):
"""
List all values for attributes with common prefix.
Args:
prefix: Common prefix to look for
walk_mro: Walk MRO to find attributes inherited from superclasses
"""

bases = cls.__mro__ if walk_mro else [cls]
return [
vars(base)[name]
for base in bases
for name in vars(base)
if name.startswith(prefix)
]


class BaseExchangeRecord(BaseRecord):
"""Represents a base record with event tracing capability."""
Expand Down
148 changes: 80 additions & 68 deletions aries_cloudagent/protocols/connections/v1_0/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,42 @@ async def create_invitation(
or_default=True,
)
image_url = self.profile.context.settings.get("image_url")
invitation = None
connection = None

invitation_mode = ConnRecord.INVITATION_MODE_ONCE
if multi_use:
invitation_mode = ConnRecord.INVITATION_MODE_MULTI

if not my_label:
my_label = self.profile.settings.get("default_label")

accept = (
ConnRecord.ACCEPT_AUTO
if (
auto_accept
or (
auto_accept is None
and self.profile.settings.get("debug.auto_accept_requests")
)
)
else ConnRecord.ACCEPT_MANUAL
)

if recipient_keys:
# TODO: register recipient keys for relay
# TODO: check that recipient keys are in wallet
invitation_key = recipient_keys[0] # TODO first key appropriate?
else:
# Create and store new invitation key
async with self.profile.session() as session:
wallet = session.inject(BaseWallet)
invitation_signing_key = await wallet.create_signing_key(
key_type=ED25519
)
invitation_key = invitation_signing_key.verkey
recipient_keys = [invitation_key]

if public:
if not self.profile.settings.get("public_invites"):
raise ConnectionManagerError("Public invitations are not enabled")
Expand All @@ -143,89 +175,64 @@ async def create_invitation(
"Cannot create public invitation with no public DID"
)

if multi_use:
raise ConnectionManagerError(
"Cannot use public and multi_use at the same time"
)

if metadata:
raise ConnectionManagerError(
"Cannot use public and set metadata at the same time"
)

# FIXME - allow ledger instance to format public DID with prefix?
invitation = ConnectionInvitation(
label=my_label, did=f"did:sov:{public_did.did}", image_url=image_url
)

connection = ConnRecord( # create connection record
invitation_key=public_did.verkey,
invitation_msg_id=invitation._id,
invitation_mode=invitation_mode,
their_role=ConnRecord.Role.REQUESTER.rfc23,
state=ConnRecord.State.INVITATION.rfc23,
accept=accept,
alias=alias,
connection_protocol=CONN_PROTO,
)

async with self.profile.session() as session:
await connection.save(session, reason="Created new invitation")

# Add mapping for multitenant relaying.
# Mediation of public keys is not supported yet
await self._route_manager.route_public_did(self.profile, public_did.verkey)

return None, invitation

invitation_mode = ConnRecord.INVITATION_MODE_ONCE
if multi_use:
invitation_mode = ConnRecord.INVITATION_MODE_MULTI

if recipient_keys:
# TODO: register recipient keys for relay
# TODO: check that recipient keys are in wallet
invitation_key = recipient_keys[0] # TODO first key appropriate?
else:
# Create and store new invitation key
# Create connection record
connection = ConnRecord(
invitation_key=invitation_key, # TODO: determine correct key to use
their_role=ConnRecord.Role.REQUESTER.rfc160,
state=ConnRecord.State.INVITATION.rfc160,
accept=accept,
invitation_mode=invitation_mode,
alias=alias,
connection_protocol=CONN_PROTO,
)
async with self.profile.session() as session:
wallet = session.inject(BaseWallet)
invitation_signing_key = await wallet.create_signing_key(
key_type=ED25519
)
invitation_key = invitation_signing_key.verkey
recipient_keys = [invitation_key]
await connection.save(session, reason="Created new invitation")

accept = (
ConnRecord.ACCEPT_AUTO
if (
auto_accept
or (
auto_accept is None
and self.profile.settings.get("debug.auto_accept_requests")
)
await self._route_manager.route_invitation(
self.profile, connection, mediation_record
)
routing_keys, my_endpoint = await self._route_manager.routing_info(
self.profile,
my_endpoint or cast(str, self.profile.settings.get("default_endpoint")),
mediation_record,
)
else ConnRecord.ACCEPT_MANUAL
)

# Create connection record
connection = ConnRecord(
invitation_key=invitation_key, # TODO: determine correct key to use
their_role=ConnRecord.Role.REQUESTER.rfc160,
state=ConnRecord.State.INVITATION.rfc160,
accept=accept,
invitation_mode=invitation_mode,
alias=alias,
connection_protocol=CONN_PROTO,
)
async with self.profile.session() as session:
await connection.save(session, reason="Created new invitation")

await self._route_manager.route_invitation(
self.profile, connection, mediation_record
)
routing_keys, my_endpoint = await self._route_manager.routing_info(
self.profile,
my_endpoint or cast(str, self.profile.settings.get("default_endpoint")),
mediation_record,
)
# Create connection invitation message
# Note: Need to split this into two stages
# to support inbound routing of invites
# Would want to reuse create_did_document and convert the result
invitation = ConnectionInvitation(
label=my_label,
recipient_keys=recipient_keys,
routing_keys=routing_keys,
endpoint=my_endpoint,
image_url=image_url,
)

# Create connection invitation message
# Note: Need to split this into two stages to support inbound routing of invites
# Would want to reuse create_did_document and convert the result
invitation = ConnectionInvitation(
label=my_label,
recipient_keys=recipient_keys,
routing_keys=routing_keys,
endpoint=my_endpoint,
image_url=image_url,
)
async with self.profile.session() as session:
await connection.attach_invitation(session, invitation)

Expand Down Expand Up @@ -529,6 +536,11 @@ async def receive_request(
their_role=ConnRecord.Role.REQUESTER.rfc160,
)
if not connection:
if not self.profile.settings.get("requests_through_public_did"):
raise ConnectionManagerError(
"Unsolicited connection requests to "
"public DID is not enabled"
)
connection = ConnRecord()
connection.invitation_key = connection_key
connection.my_did = my_info.did
Expand Down
Loading

0 comments on commit 0c93947

Please sign in to comment.