Skip to content

Commit 1460b2d

Browse files
authoredNov 10, 2021
Merge branch 'main' into main
2 parents 60f3538 + 576bcad commit 1460b2d

File tree

17 files changed

+2865
-196
lines changed

17 files changed

+2865
-196
lines changed
 

Diff for: ‎aries_cloudagent/config/argparse.py

+33-4
Original file line numberDiff line numberDiff line change
@@ -1535,15 +1535,25 @@ def add_arguments(self, parser: ArgumentParser):
15351535
"directly."
15361536
),
15371537
)
1538+
parser.add_argument(
1539+
"--endorser-invitation",
1540+
type=str,
1541+
metavar="<endorser-invitation>",
1542+
env_var="ACAPY_ENDORSER_INVITATION",
1543+
help=(
1544+
"For transaction Authors, specify the invitation used to "
1545+
"connect to the Endorser agent who will be endorsing transactions. "
1546+
"Note this is a multi-use invitation created by the Endorser agent."
1547+
),
1548+
)
15381549
parser.add_argument(
15391550
"--endorser-public-did",
15401551
type=str,
15411552
metavar="<endorser-public-did>",
15421553
env_var="ACAPY_ENDORSER_PUBLIC_DID",
15431554
help=(
1544-
"For transaction Authors, specify the the public DID of the Endorser "
1545-
"agent who will be endorsing transactions. Note this requires that "
1546-
"the connection be made using the Endorser's public DID."
1555+
"For transaction Authors, specify the public DID of the Endorser "
1556+
"agent who will be endorsing transactions."
15471557
),
15481558
)
15491559
parser.add_argument(
@@ -1552,7 +1562,7 @@ def add_arguments(self, parser: ArgumentParser):
15521562
metavar="<endorser-alias>",
15531563
env_var="ACAPY_ENDORSER_ALIAS",
15541564
help=(
1555-
"For transaction Authors, specify the the alias of the Endorser "
1565+
"For transaction Authors, specify the alias of the Endorser "
15561566
"connection that will be used to endorse transactions."
15571567
),
15581568
)
@@ -1623,6 +1633,25 @@ def get_settings(self, args: Namespace):
16231633
"Authors"
16241634
)
16251635

1636+
if args.endorser_invitation:
1637+
if settings["endorser.author"]:
1638+
if not settings.get("endorser.endorser_public_did"):
1639+
raise ArgsParseError(
1640+
"Parameter --endorser-public-did must be provided if "
1641+
"--endorser-invitation is set."
1642+
)
1643+
if not settings.get("endorser.endorser_alias"):
1644+
raise ArgsParseError(
1645+
"Parameter --endorser-alias must be provided if "
1646+
"--endorser-invitation is set."
1647+
)
1648+
settings["endorser.endorser_invitation"] = args.endorser_invitation
1649+
else:
1650+
raise ArgsParseError(
1651+
"Parameter --endorser-invitation should only be set for transaction "
1652+
"Authors"
1653+
)
1654+
16261655
if args.auto_request_endorsement:
16271656
if settings["endorser.author"]:
16281657
settings["endorser.auto_request"] = True

Diff for: ‎aries_cloudagent/protocols/coordinate_mediation/mediation_invite_store.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,15 @@
55
Enables having the mediation invite config be the same
66
for `provision` and `starting` commands.
77
"""
8-
import dataclasses
98
import json
10-
from typing import Optional
9+
from typing import NamedTuple, Optional
1110

1211
from aries_cloudagent.storage.base import BaseStorage
1312
from aries_cloudagent.storage.error import StorageNotFoundError
1413
from aries_cloudagent.storage.record import StorageRecord
1514

1615

17-
@dataclasses.dataclass
18-
class MediationInviteRecord:
16+
class MediationInviteRecord(NamedTuple):
1917
"""A record to store mediation invites and their freshness."""
2018

2119
invite: str

Diff for: ‎aries_cloudagent/protocols/endorse_transaction/v1_0/routes.py

+88-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Endorse Transaction handling admin routes."""
22

33
import json
4+
import logging
45

56
from aiohttp import web
67
from aiohttp_apispec import (
@@ -22,11 +23,20 @@
2223
from ....messaging.models.base import BaseModelError
2324
from ....messaging.models.openapi import OpenAPISchema
2425
from ....messaging.valid import UUIDFour
26+
from ....protocols.connections.v1_0.manager import ConnectionManager
27+
from ....protocols.connections.v1_0.messages.connection_invitation import (
28+
ConnectionInvitation,
29+
)
30+
from ....protocols.out_of_band.v1_0.manager import OutOfBandManager
31+
from ....protocols.out_of_band.v1_0.messages.invitation import InvitationMessage
2532
from ....storage.error import StorageError, StorageNotFoundError
2633

2734
from .manager import TransactionManager, TransactionManagerError
2835
from .models.transaction_record import TransactionRecord, TransactionRecordSchema
2936
from .transaction_jobs import TransactionJob
37+
from .util import is_author_role, get_endorser_connection_id
38+
39+
LOGGER = logging.getLogger(__name__)
3040

3141

3242
class TransactionListSchema(OpenAPISchema):
@@ -705,13 +715,88 @@ def register_events(event_bus: EventBus):
705715

706716
async def on_startup_event(profile: Profile, event: Event):
707717
"""Handle any events we need to support."""
708-
print(">>> TODO Received STARTUP event")
709-
pass
718+
719+
# auto setup is only for authors
720+
if not is_author_role(profile):
721+
return
722+
723+
# see if we have an invitation to connect to the endorser
724+
endorser_invitation = profile.settings.get_value("endorser.endorser_invitation")
725+
if not endorser_invitation:
726+
# no invitation, we can't connect automatically
727+
return
728+
729+
# see if we need to initiate an endorser connection
730+
endorser_alias = profile.settings.get_value("endorser.endorser_alias")
731+
if not endorser_alias:
732+
# no alias is specified for the endorser connection
733+
# note that alias is required if invitation is specified
734+
return
735+
736+
connection_id = await get_endorser_connection_id(profile)
737+
if connection_id:
738+
# there is already a connection
739+
return
740+
741+
endorser_did = profile.settings.get_value("endorser.endorser_public_did")
742+
if not endorser_did:
743+
# no DID, we can connect but we can't properly setup the connection metadata
744+
# note that DID is required if invitation is specified
745+
return
746+
747+
try:
748+
# OK, we are an author, we have no endorser connection but we have enough info
749+
# to automatically initiate the connection
750+
invite = InvitationMessage.from_url(endorser_invitation)
751+
if invite:
752+
oob_mgr = OutOfBandManager(profile)
753+
conn_record = await oob_mgr.receive_invitation(
754+
invitation=invite,
755+
auto_accept=True,
756+
alias=endorser_alias,
757+
)
758+
else:
759+
invite = ConnectionInvitation.from_url(endorser_invitation)
760+
if invite:
761+
conn_mgr = ConnectionManager(profile)
762+
conn_record = await conn_mgr.receive_invitation(
763+
invitation=invite,
764+
auto_accept=True,
765+
alias=endorser_alias,
766+
)
767+
else:
768+
raise Exception(
769+
"Failed to establish endorser connection, invalid "
770+
"invitation format."
771+
)
772+
773+
# configure the connection role and info (don't need to wait for the connection)
774+
transaction_mgr = TransactionManager(profile)
775+
await transaction_mgr.set_transaction_my_job(
776+
record=conn_record,
777+
transaction_my_job=TransactionJob.TRANSACTION_AUTHOR.name,
778+
)
779+
780+
async with profile.session() as session:
781+
value = await conn_record.metadata_get(session, "endorser_info")
782+
if value:
783+
value["endorser_did"] = endorser_did
784+
value["endorser_name"] = endorser_alias
785+
else:
786+
value = {"endorser_did": endorser_did, "endorser_name": endorser_alias}
787+
await conn_record.metadata_set(session, key="endorser_info", value=value)
788+
789+
except Exception as e:
790+
# log the error, but continue
791+
LOGGER.exception(
792+
"Error accepting endorser invitation/configuring endorser connection: %s",
793+
str(e),
794+
)
710795

711796

712797
async def on_shutdown_event(profile: Profile, event: Event):
713798
"""Handle any events we need to support."""
714-
print(">>> TODO Received SHUTDOWN event")
799+
# nothing to do for now ...
715800
pass
716801

717802

Diff for: ‎aries_cloudagent/protocols/present_proof/dif/pres_exch.py

+79-5
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class Meta:
146146
_from = fields.Str(description="From", required=False, data_key="from")
147147
# Self References
148148
from_nested = fields.List(
149-
fields.Nested(lambda: SubmissionRequirementsSchema(exclude=("from_nested",))),
149+
fields.Nested(lambda: SubmissionRequirementsSchema()),
150150
required=False,
151151
)
152152

@@ -180,7 +180,7 @@ def __init__(
180180
uri: str = None,
181181
required: bool = None,
182182
):
183-
"""Initialize InputDescriptors."""
183+
"""Initialize SchemaInputDescriptor."""
184184
self.uri = uri
185185
self.required = required
186186

@@ -201,6 +201,59 @@ class Meta:
201201
required = fields.Bool(description="Required", required=False)
202202

203203

204+
class SchemasInputDescriptorFilter(BaseModel):
205+
"""SchemasInputDescriptorFilter."""
206+
207+
class Meta:
208+
"""InputDescriptor Schemas filter metadata."""
209+
210+
schema_class = "SchemasInputDescriptorFilterSchema"
211+
212+
def __init__(
213+
self,
214+
*,
215+
oneof_filter: bool = False,
216+
uri_groups: Sequence[Sequence[SchemaInputDescriptor]] = None,
217+
):
218+
"""Initialize SchemasInputDescriptorFilter."""
219+
self.oneof_filter = oneof_filter
220+
self.uri_groups = uri_groups
221+
222+
223+
class SchemasInputDescriptorFilterSchema(BaseModelSchema):
224+
"""Single SchemasInputDescriptorFilterSchema Schema."""
225+
226+
class Meta:
227+
"""SchemasInputDescriptorFilterSchema metadata."""
228+
229+
model_class = SchemasInputDescriptorFilter
230+
unknown = EXCLUDE
231+
232+
uri_groups = fields.List(fields.List(fields.Nested(SchemaInputDescriptorSchema)))
233+
oneof_filter = fields.Bool(description="oneOf")
234+
235+
@pre_load
236+
def extract_info(self, data, **kwargs):
237+
"""deserialize."""
238+
new_data = {}
239+
if isinstance(data, dict):
240+
if "oneof_filter" in data:
241+
new_data["oneof_filter"] = True
242+
uri_group_list_of_list = []
243+
uri_group_list = data.get("oneof_filter")
244+
for uri_group in uri_group_list:
245+
if isinstance(uri_group, list):
246+
uri_group_list_of_list.append(uri_group)
247+
else:
248+
uri_group_list_of_list.append([uri_group])
249+
new_data["uri_groups"] = uri_group_list_of_list
250+
elif isinstance(data, list):
251+
new_data["oneof_filter"] = False
252+
new_data["uri_groups"] = [data]
253+
data = new_data
254+
return data
255+
256+
204257
class DIFHolder(BaseModel):
205258
"""Single Holder object for Constraints."""
206259

@@ -548,7 +601,7 @@ def __init__(
548601
purpose: str = None,
549602
metadata: dict = None,
550603
constraint: Constraints = None,
551-
schemas: Sequence[SchemaInputDescriptor] = None,
604+
schemas: SchemasInputDescriptorFilter = None,
552605
):
553606
"""Initialize InputDescriptors."""
554607
self.id = id
@@ -584,8 +637,29 @@ class Meta:
584637
constraint = fields.Nested(
585638
ConstraintsSchema, required=False, data_key="constraints"
586639
)
587-
schemas = fields.List(
588-
fields.Nested(SchemaInputDescriptorSchema), required=False, data_key="schema"
640+
schemas = fields.Nested(
641+
SchemasInputDescriptorFilterSchema,
642+
required=False,
643+
data_key="schema",
644+
description=(
645+
"Accepts a list of schema or a dict containing filters like oneof_filter."
646+
),
647+
example=(
648+
{
649+
"oneOf": [
650+
[
651+
{"uri": "https://www.w3.org/Test1#Test1"},
652+
{"uri": "https://www.w3.org/Test2#Test2"},
653+
],
654+
{
655+
"oneof_filter": [
656+
[{"uri": "https://www.w3.org/Test1#Test1"}],
657+
[{"uri": "https://www.w3.org/Test2#Test2"}],
658+
]
659+
},
660+
]
661+
}
662+
),
589663
)
590664

591665

0 commit comments

Comments
 (0)
Please sign in to comment.