Skip to content

Commit 0c93947

Browse files
authored
Merge branch 'main' into feature/create-all-registered-dids
2 parents c29d725 + 748ec9c commit 0c93947

11 files changed

Lines changed: 374 additions & 158 deletions

File tree

aries_cloudagent/config/argparse.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,17 @@ def add_arguments(self, parser: ArgumentParser):
10391039
action="store_true",
10401040
env_var="ACAPY_PUBLIC_INVITES",
10411041
help=(
1042-
"Send invitations out, and receive connection requests, "
1042+
"Send invitations out using the public DID for the agent, "
1043+
"and receive connection requests solicited by invitations "
1044+
"which use the public DID. Default: false."
1045+
),
1046+
)
1047+
parser.add_argument(
1048+
"--requests-through-public-did",
1049+
action="store_true",
1050+
env_var="ACAPY_REQUESTS_THROUGH_PUBLIC_DID",
1051+
help=(
1052+
"Allow agent to receive unsolicited connection requests, "
10431053
"using the public DID for the agent. Default: false."
10441054
),
10451055
)
@@ -1134,6 +1144,13 @@ def get_settings(self, args: Namespace) -> dict:
11341144
settings["monitor_forward"] = args.monitor_forward
11351145
if args.public_invites:
11361146
settings["public_invites"] = True
1147+
if args.requests_through_public_did:
1148+
if not args.public_invites:
1149+
raise ArgsParseError(
1150+
"--public-invites is required to use "
1151+
"--requests-through-public-did"
1152+
)
1153+
settings["requests_through_public_did"] = True
11371154
if args.timing:
11381155
settings["timing.enabled"] = True
11391156
if args.timing_log:

aries_cloudagent/connections/models/conn_record.py

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -677,11 +677,7 @@ class Meta:
677677
required=False,
678678
description="Routing state of connection",
679679
validate=validate.OneOf(
680-
[
681-
getattr(ConnRecord, m)
682-
for m in vars(ConnRecord)
683-
if m.startswith("ROUTING_STATE_")
684-
]
680+
ConnRecord.get_attributes_by_prefix("ROUTING_STATE_", walk_mro=False)
685681
),
686682
example=ConnRecord.ROUTING_STATE_ACTIVE,
687683
)
@@ -690,11 +686,7 @@ class Meta:
690686
description="Connection acceptance: manual or auto",
691687
example=ConnRecord.ACCEPT_AUTO,
692688
validate=validate.OneOf(
693-
[
694-
getattr(ConnRecord, a)
695-
for a in vars(ConnRecord)
696-
if a.startswith("ACCEPT_")
697-
]
689+
ConnRecord.get_attributes_by_prefix("ACCEPT_", walk_mro=False)
698690
),
699691
)
700692
error_msg = fields.Str(
@@ -707,11 +699,7 @@ class Meta:
707699
description="Invitation mode",
708700
example=ConnRecord.INVITATION_MODE_ONCE,
709701
validate=validate.OneOf(
710-
[
711-
getattr(ConnRecord, i)
712-
for i in vars(ConnRecord)
713-
if i.startswith("INVITATION_MODE_")
714-
]
702+
ConnRecord.get_attributes_by_prefix("INVITATION_MODE_", walk_mro=False)
715703
),
716704
)
717705
alias = fields.Str(

aries_cloudagent/messaging/models/base_record.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class Meta:
8181
EVENT_NAMESPACE: str = "acapy::record"
8282
LOG_STATE_FLAG = None
8383
TAG_NAMES = {"state"}
84+
STATE_DELETED = "deleted"
8485

8586
def __init__(
8687
self,
@@ -420,7 +421,7 @@ async def delete_record(self, session: ProfileSession):
420421
storage = session.inject(BaseStorage)
421422
if self.state:
422423
self._previous_state = self.state
423-
self.state = "deleted"
424+
self.state = BaseRecord.STATE_DELETED
424425
await self.emit_event(session, self.serialize())
425426
await storage.delete_record(self.storage_record)
426427

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

501+
@classmethod
502+
def get_attributes_by_prefix(cls, prefix: str, walk_mro: bool = True):
503+
"""
504+
List all values for attributes with common prefix.
505+
506+
Args:
507+
prefix: Common prefix to look for
508+
walk_mro: Walk MRO to find attributes inherited from superclasses
509+
"""
510+
511+
bases = cls.__mro__ if walk_mro else [cls]
512+
return [
513+
vars(base)[name]
514+
for base in bases
515+
for name in vars(base)
516+
if name.startswith(prefix)
517+
]
518+
500519

501520
class BaseExchangeRecord(BaseRecord):
502521
"""Represents a base record with event tracing capability."""

aries_cloudagent/protocols/connections/v1_0/manager.py

Lines changed: 80 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,42 @@ async def create_invitation(
127127
or_default=True,
128128
)
129129
image_url = self.profile.context.settings.get("image_url")
130+
invitation = None
131+
connection = None
132+
133+
invitation_mode = ConnRecord.INVITATION_MODE_ONCE
134+
if multi_use:
135+
invitation_mode = ConnRecord.INVITATION_MODE_MULTI
130136

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

140+
accept = (
141+
ConnRecord.ACCEPT_AUTO
142+
if (
143+
auto_accept
144+
or (
145+
auto_accept is None
146+
and self.profile.settings.get("debug.auto_accept_requests")
147+
)
148+
)
149+
else ConnRecord.ACCEPT_MANUAL
150+
)
151+
152+
if recipient_keys:
153+
# TODO: register recipient keys for relay
154+
# TODO: check that recipient keys are in wallet
155+
invitation_key = recipient_keys[0] # TODO first key appropriate?
156+
else:
157+
# Create and store new invitation key
158+
async with self.profile.session() as session:
159+
wallet = session.inject(BaseWallet)
160+
invitation_signing_key = await wallet.create_signing_key(
161+
key_type=ED25519
162+
)
163+
invitation_key = invitation_signing_key.verkey
164+
recipient_keys = [invitation_key]
165+
134166
if public:
135167
if not self.profile.settings.get("public_invites"):
136168
raise ConnectionManagerError("Public invitations are not enabled")
@@ -143,89 +175,64 @@ async def create_invitation(
143175
"Cannot create public invitation with no public DID"
144176
)
145177

146-
if multi_use:
147-
raise ConnectionManagerError(
148-
"Cannot use public and multi_use at the same time"
149-
)
150-
151-
if metadata:
152-
raise ConnectionManagerError(
153-
"Cannot use public and set metadata at the same time"
154-
)
155-
156178
# FIXME - allow ledger instance to format public DID with prefix?
157179
invitation = ConnectionInvitation(
158180
label=my_label, did=f"did:sov:{public_did.did}", image_url=image_url
159181
)
160182

183+
connection = ConnRecord( # create connection record
184+
invitation_key=public_did.verkey,
185+
invitation_msg_id=invitation._id,
186+
invitation_mode=invitation_mode,
187+
their_role=ConnRecord.Role.REQUESTER.rfc23,
188+
state=ConnRecord.State.INVITATION.rfc23,
189+
accept=accept,
190+
alias=alias,
191+
connection_protocol=CONN_PROTO,
192+
)
193+
194+
async with self.profile.session() as session:
195+
await connection.save(session, reason="Created new invitation")
196+
161197
# Add mapping for multitenant relaying.
162198
# Mediation of public keys is not supported yet
163199
await self._route_manager.route_public_did(self.profile, public_did.verkey)
164200

165-
return None, invitation
166-
167-
invitation_mode = ConnRecord.INVITATION_MODE_ONCE
168-
if multi_use:
169-
invitation_mode = ConnRecord.INVITATION_MODE_MULTI
170-
171-
if recipient_keys:
172-
# TODO: register recipient keys for relay
173-
# TODO: check that recipient keys are in wallet
174-
invitation_key = recipient_keys[0] # TODO first key appropriate?
175201
else:
176-
# Create and store new invitation key
202+
# Create connection record
203+
connection = ConnRecord(
204+
invitation_key=invitation_key, # TODO: determine correct key to use
205+
their_role=ConnRecord.Role.REQUESTER.rfc160,
206+
state=ConnRecord.State.INVITATION.rfc160,
207+
accept=accept,
208+
invitation_mode=invitation_mode,
209+
alias=alias,
210+
connection_protocol=CONN_PROTO,
211+
)
177212
async with self.profile.session() as session:
178-
wallet = session.inject(BaseWallet)
179-
invitation_signing_key = await wallet.create_signing_key(
180-
key_type=ED25519
181-
)
182-
invitation_key = invitation_signing_key.verkey
183-
recipient_keys = [invitation_key]
213+
await connection.save(session, reason="Created new invitation")
184214

185-
accept = (
186-
ConnRecord.ACCEPT_AUTO
187-
if (
188-
auto_accept
189-
or (
190-
auto_accept is None
191-
and self.profile.settings.get("debug.auto_accept_requests")
192-
)
215+
await self._route_manager.route_invitation(
216+
self.profile, connection, mediation_record
217+
)
218+
routing_keys, my_endpoint = await self._route_manager.routing_info(
219+
self.profile,
220+
my_endpoint or cast(str, self.profile.settings.get("default_endpoint")),
221+
mediation_record,
193222
)
194-
else ConnRecord.ACCEPT_MANUAL
195-
)
196-
197-
# Create connection record
198-
connection = ConnRecord(
199-
invitation_key=invitation_key, # TODO: determine correct key to use
200-
their_role=ConnRecord.Role.REQUESTER.rfc160,
201-
state=ConnRecord.State.INVITATION.rfc160,
202-
accept=accept,
203-
invitation_mode=invitation_mode,
204-
alias=alias,
205-
connection_protocol=CONN_PROTO,
206-
)
207-
async with self.profile.session() as session:
208-
await connection.save(session, reason="Created new invitation")
209223

210-
await self._route_manager.route_invitation(
211-
self.profile, connection, mediation_record
212-
)
213-
routing_keys, my_endpoint = await self._route_manager.routing_info(
214-
self.profile,
215-
my_endpoint or cast(str, self.profile.settings.get("default_endpoint")),
216-
mediation_record,
217-
)
224+
# Create connection invitation message
225+
# Note: Need to split this into two stages
226+
# to support inbound routing of invites
227+
# Would want to reuse create_did_document and convert the result
228+
invitation = ConnectionInvitation(
229+
label=my_label,
230+
recipient_keys=recipient_keys,
231+
routing_keys=routing_keys,
232+
endpoint=my_endpoint,
233+
image_url=image_url,
234+
)
218235

219-
# Create connection invitation message
220-
# Note: Need to split this into two stages to support inbound routing of invites
221-
# Would want to reuse create_did_document and convert the result
222-
invitation = ConnectionInvitation(
223-
label=my_label,
224-
recipient_keys=recipient_keys,
225-
routing_keys=routing_keys,
226-
endpoint=my_endpoint,
227-
image_url=image_url,
228-
)
229236
async with self.profile.session() as session:
230237
await connection.attach_invitation(session, invitation)
231238

@@ -529,6 +536,11 @@ async def receive_request(
529536
their_role=ConnRecord.Role.REQUESTER.rfc160,
530537
)
531538
if not connection:
539+
if not self.profile.settings.get("requests_through_public_did"):
540+
raise ConnectionManagerError(
541+
"Unsolicited connection requests to "
542+
"public DID is not enabled"
543+
)
532544
connection = ConnRecord()
533545
connection.invitation_key = connection_key
534546
connection.my_did = my_info.did

0 commit comments

Comments
 (0)