Skip to content

Commit d1ce046

Browse files
authored
Merge branch 'main' into feature/standalone-docker
2 parents 72185aa + d97f71d commit d1ce046

42 files changed

Lines changed: 749 additions & 236 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ repos:
1212
hooks:
1313
- id: black
1414
stages: [commit]
15-
- repo: https://gitlab.com/pycqa/flake8
15+
- repo: https://github.com/pycqa/flake8.git
1616
rev: 3.9.0
1717
hooks:
1818
- id: flake8

DIDMethods.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# DID methods in ACA-Py
2+
Decentralized Identifiers, or DIDs, are URIs that point to documents that describe cryptographic primitives and protocols used in decentralized identity management.
3+
DIDs include methods that describe where and how documents can be retrieved.
4+
DID methods support specific types of keys and may or may not require the holder to specify the DID itself.
5+
6+
ACA-Py provides a `DIDMethods` registry holding all the DID methods supported for storage in a wallet
7+
8+
> :warning: Askar and InMemory are the only wallets supporting this registry.
9+
10+
## Registering a DID method
11+
By default, ACA-Py supports `did:key` and `did:sov`.
12+
Plugins can register DID additional methods to make them available to holders.
13+
Here's a snippet adding support for `did:web` to the registry from a plugin `setup` method.
14+
15+
```python=
16+
WEB = DIDMethod(
17+
name="web",
18+
key_types=[ED25519, BLS12381G2],
19+
rotation=True,
20+
holder_defined_did=HolderDefinedDid.REQUIRED # did:web is not derived from key material but from a user-provided respository name
21+
)
22+
23+
async def setup(context: InjectionContext):
24+
methods = context.inject(DIDMethods)
25+
methods.register(WEB)
26+
```
27+
28+
## Creating a DID
29+
30+
`POST /wallet/did/create` can be provided with parameters for any registered DID method. Here's a follow-up to the
31+
`did:web` method example:
32+
33+
```json=
34+
{
35+
"method": "web",
36+
"options": {
37+
"did": "did:web:doma.in",
38+
"key_type": "ed25519"
39+
}
40+
}
41+
```
42+
43+
## Resolving DIDs
44+
45+
For specifics on how DIDs are resolved in ACA-Py, see: [DID Resolution](DIDResolution.md).

aries_cloudagent/admin/server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ async def setup_context(request: web.Request, handler):
433433
)
434434

435435
server_routes = [
436-
web.get("/", self.redirect_handler, allow_head=False),
436+
web.get("/", self.redirect_handler, allow_head=True),
437437
web.get("/plugins", self.plugins_handler, allow_head=False),
438438
web.get("/status", self.status_handler, allow_head=False),
439439
web.get("/status/config", self.config_handler, allow_head=False),

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/core/tests/test_conductor.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
from ...utils.stats import Collector
3737
from ...version import __version__
3838
from ...wallet.base import BaseWallet
39-
from ...wallet.did_method import SOV
39+
from ...wallet.did_method import SOV, DIDMethods
4040
from ...wallet.key_type import ED25519
4141
from .. import conductor as test_module
4242

@@ -87,6 +87,7 @@ async def build_context(self) -> InjectionContext:
8787
context.injector.bind_instance(ProfileManager, InMemoryProfileManager())
8888
context.injector.bind_instance(ProtocolRegistry, ProtocolRegistry())
8989
context.injector.bind_instance(BaseWireFormat, self.wire_format)
90+
context.injector.bind_instance(DIDMethods, DIDMethods())
9091
context.injector.bind_instance(DIDResolver, DIDResolver([]))
9192
context.injector.bind_instance(EventBus, MockEventBus())
9293
return context

aries_cloudagent/ledger/tests/test_indy_vdr.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
@pytest.fixture()
3030
def ledger():
31-
profile = InMemoryProfile.test_profile()
31+
profile = InMemoryProfile.test_profile(bind={DIDMethods: DIDMethods()})
3232
ledger = IndyVdrLedger(IndyVdrLedgerPool("test-ledger"), profile)
3333

3434
async def open():

aries_cloudagent/messaging/jsonld/tests/test_routes.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from ....resolver.did_resolver import DIDResolver
1515
from ....vc.ld_proofs.document_loader import DocumentLoader
1616
from ....wallet.base import BaseWallet
17-
from ....wallet.did_method import SOV
17+
from ....wallet.did_method import SOV, DIDMethods
1818
from ....wallet.error import WalletError
1919
from ....wallet.key_type import ED25519
2020
from ..error import (
@@ -274,6 +274,7 @@ async def setUp(self):
274274
self.context.profile.context.injector.bind_instance(
275275
DocumentLoader, custom_document_loader
276276
)
277+
self.context.profile.context.injector.bind_instance(DIDMethods, DIDMethods())
277278
self.did_info = await (await self.context.session()).wallet.create_local_did(
278279
SOV, ED25519
279280
)

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."""

0 commit comments

Comments
 (0)